删除链表中重复的节点(经典链表面试题)

题目

删除链表中重复的节点
在一个排序,如何删除重复的节点?
例如:1 -> 2 -> 3 -> 3 -> 4
删除后是 1 -> 2 -> 4

分析

在链表的面试题中,重要一点是要注意对链表的情况要全部考虑到,或许你在看到上面的例子后按照给出的例子大笔一挥,写出代码后,那么你很可能就已经凉凉。。。

首先我们应该考虑大部分情况,也就是上面例子的情况,但是我们不能急于下笔,应该再考虑,如果在传参的时候,调用者传NULL?在处理链表的过程中,如果链表有一个节点会怎么样,两个呢?要是没有重复的节点呢?如果全是重复的节点呢?还要注意,在没带头节点的情况中,头节点怎么处理?
想完这么多可能会觉得这么多情况怎么处理,不要急,我们一项一项的写,写的时候要注意自己意识到的情况,我们可以用写注释的方式,先写出情况。

对这道题我们来写一下代码~ 分析分析
写链表中我们知道对于单链表访问最大的缺点就是只能单向,从前向后,所以我们删除也是一样,删除当前就得知道当前的上一个节点,那么我们这里采用指针记录的方式。

方法1(粗暴)

我们采用最容易想到的方法,不用考虑时间效率与空间效率问题,就直接用遍历,用一个哈希表结构来记录每个节点对应的值在节点中出现了几次,统计完成后,然后通过哈希表中如果出现次数为大于等于2,再用两个指针一个前一个后开始找对应的值就行了,如果后一个指针指向的节点的值为哈希表中出现次数大于等于2的数,那么前一个指针指向后一个指针的next然后删除后一个指针所指向的节点,让后一个指针重新指向前一个指针的next,就这样就可以完成删除。

虽然这个方法可以,但是时间复杂度为O(N), 空间复杂度为一个哈希表的结构,所以采用这种不是最佳选择。

方法2(三指针法)

采用三个指针来进行遍历,同时删除重复的节点,因为是有序的链表,我们就可以确定,重复的元素肯定是在一块链接,所以我们就可以,用三指针,我们这里就叫
pre、cur、nex 分别代表的是前中后三个指针,我们在考虑的情况中,如果头节点开始就重复,我们就处理很起来多了一种情况就需要额外处理,所以我们添加一个头节点,变成带头节点,保证了头节点开始不会重复,那么我们就可以开是让pre指向带头的节点,cur指向pre的next,nex指向cur的next。

接下来我们就可以看cur是否和nex相等,相等就让nex继续向下走,不相等然后再处理删除,cur开始到nex中间节点都是要删除的(包含cur指向,不包含nex指向)删除,就用到了pre,删除完成让pre指向cur就可以了。

如果cur值与nex值不相等,那么就可以三个指针各自往前移动一个。

下来我来看代码~

ListNode* deleteDuplication(ListNode* pHead)
{
    // 先判断空
    if (pHead == NULL)
    {
        return NULL;
    }
    // 判断是否只有一个节点
    if (pHead->next == NULL)
    {
        return pHead;
    }
    // 我们采用带头链表,自己添加一个头
    ListNode* pre = new ListNode(int());
    pre->next = pHead; // 把头节点链接在链表上
    ListNode* pre_head = pre; // 用来保存头节点,用于返回删除后的链表
    ListNode* cur = pHead; //中指针
    ListNode* nex = pHead->next; // 后面指针
    while (nex != NULL) // 结束条件
        while (nex != NULL && cur->val == nex->val) 
        {
            nex = nex->next;
        }
        // 如果没有重复的那么cur的next一定等于nex
        if (cur->next != nex) // 如果相等说明没有相同的节点
        {
            while (cur != nex) // 删除动作
            {
                pre->next = cur->next;
                delete cur;
                cur = pre->next;
            }
            if (nex != NULL) // 这里一定要要注意,要防止走到NULL发生段错误
            nex = nex->next;
        }
        else
        {
            // 处理没有重复的情况
            pre = cur;
            nex = nex->next;
            cur = cur->next;
        }
    }
    ListNode* head = pre_head->next; // 释放空间,防止内存泄漏
    delete pre_head;
    return head;
}

我们测试要尽可能的全面
1)传NULL
2)只有一个节点
3)头节点开始就有重复
4)中间节点重复
5)尾部节点重复
6)链表中没有重复链表
7)所有节点都是重复的

扫描二维码关注公众号,回复: 2023749 查看本文章

猜你喜欢

转载自blog.csdn.net/gangstudyit/article/details/80623477