链表去重的两种解决思路

363 阅读3分钟

链表去重

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

题目测评链接:leetcode.cn/problems/re…

思路1:

暴力解法,确定一个结点,遍历该节点后面的链表,是否有重复元素出现的结点,如果有就删除该节点。

时间复杂度:O(n 2) 空间复杂度:O(1)

class Solution {
public:
    ListNode* removeDuplicateNodes(ListNode* head) {
       	ListNode* p,* q,*r;
    	p = head;
    	while(p != NULL){
    		q = p;
    		while(q->next != NULL){
    			if(p->val == q->next->val){
    				r = q->next;
    				q->next = r->next;
    				// free(r);	
    			}else{
    				q = q->next;
    			}
    		}
    		p = p->next;
    	}
    	return head;
    }
};

思路2:

如果题目限定了 结点元素值的范围,比如说 [0, 10000],我们只需要开一个 10001长度大小的bool数组 st,遍历的同时记录元素是否出现过,如果没有出现过,即st[p -> val] == false,st[p -> val] 设置为true,代表已经出现过,后面如果遇到相同存在的元素,即可删除

时间复杂度:O(n),空间复杂度:O(m)

m 表示元素的值域,比如[0, 10000] 需要开10001大小的数组,如果[0, 1e9] 需要开1e9 + 1 大小的数组

class Solution {
public:
    ListNode* removeDuplicateNodes(ListNode* head) {
       	if(head == NULL) return head;
        bool st[20010];
        memset(st, false, sizeof st); // 将标记数组的全部元素初始化为false
        st[head->val] = true; // 头结点设置为true
        ListNode* p = head;
        while(p->next != NULL){
            int x = p -> next->val;
            // 值为true,表示该元素出现过,要删除该结点
            if(st[x]){
                ListNode* q = p->next;
                p->next = q->next;
                // free(q);
            }
            // 不存在,访问下一个元素
            else{
                st[x] = true;
                p = p->next;
            }
        }
        return head;
    }
};

进阶:

如果题目没有限定结点元素值的范围,而实际笔试的时候又不能直接调用 map, set等哈希集合这些stl容器,就需要手写 哈希表来实现查询和插入的操作

哈希表查询和插入元素,时间复杂度默认为 O1

手写哈希表,一般分为两种,开放定址法(线性探测或者二次探测),链地址法,两种都有之前手写好的模版直接套即可。

最终实现效果如下:

时间复杂度:O(n),空间复杂度:O(n)

const int N = 200003;
ListNode *ht[N];
bool find(int x){ // 哈希表查询x是否存在
    int t = (x % N + N) % N;
    for(auto p = ht[t]; p; p = p->next){
        if(p->val == x)
            return true;
    }
    return false;
}

void add(int a, int b){ // 单链表ht[a]中头插元素b
    auto p = new ListNode(b);
    p -> next = ht[a];
    ht[a] = p;
}

void insert(int x){ // 单链表中插入x
    if(find(x)) return;
    int t = (x % N + N) % N;
    add(t, x);
}
class Solution {
public:    
    ListNode* removeDuplicateNodes(ListNode* head) {
       	if(head == NULL) return head;
        for(int i = 0; i < 20000; i ++) ht[i] = NULL; // 清空结构体数组
        insert(head -> val); // 插入头结点
        ListNode* p = head;
        while(p -> next != NULL){
            int x = p -> next->val;
            // 值为true,表示该元素出现过,要删除该结点
            if(find(x)){
                ListNode* q = p->next;
                p->next = q->next;
                // free(q);
            }
            // x不存在,访问下一个元素
            else{
                insert(x);
                p = p->next;
            }
        }
        return head;
    }
};