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

题目描述
题目链接:删除链表中重复的节点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

举个例子
在这里插入图片描述

解题思路:
删除掉重复节点,这个和去重有些不一样,去重是保留一个,这里是要有重复的完全删除掉。第一感觉就使用双指针,因为肯定要进行前后比较。两个节点对应的值不相同就往后走,相同就让后面指针自己走,直到不相同。然后删除掉之前的节点。

图解实现:
定义两个指针cur和next进行指向,假如两者对应的值不相等,就同时向后走
在这里插入图片描述
假如走到curnext对应的值相等之后停下cur,让next继续走直到两者的值重新不相等在这里插入图片描述
这时候我遇到的问题是,假如保存cur的下一个节点,循环进行free。那么1,2两个几点怎么办,所以再次定义一个prev节点用于保存节点2。
这样我们就可以放心的free掉重复的值,再让prev->next直接指向next就行了。在这里插入图片描述
当然进行前一步操作之后,next也要移动到下一个直至next为空

在这里插入图片描述
走到这其实我们主要步骤已经完成,中间部分的逻辑是没有问题的,但是单链表我们必须考虑它的极端情况,只要中间逻辑不错,出问题只会出在头和尾
先看头的情况:
在这里插入图片描述
如果像上图一样free掉cur,那么此时prev还是空,NULL->Next=Next,你对空指针进行了操作。
再看第二种尾的情况:
在这里插入图片描述
在这里插入图片描述
现在我们对第一种头的情况处理:
经过前面分析我们确定是prev->next=next;出了问题,那么我们现在对它进行操作,他是空出了问题
我们就写一条if语句,if不为空,正常进行操作,else为空处理它;

             if(prev)
             {
    
    
                prev->next=next;
             }
                //这是极端情况1,假如头结点重复导致走到这里prev还是空
                else
                {
    
    
                    //让next这个位置成为头
                   pHead=next;
                }

现在对第二种尾的情况分析:
经过前面作图,尾重复的时候,由于迭代条件next=next->next,一直让他运行了下去,那么我们设置一个条件让他为NULL的时候就不要进循环了

//假如是尾上重复,因为是再循环里,next走到停不下来,我们让他NULL时候不进while,就停下了
                while(next && cur->val == next->val)

尾上重复的情况,走到最后一次进循环

//尾上重复的情况,最后一次进循环这里是空
                    next=next->next;

next为空,这一步就不要运行了,因为他是为了下一步后面如果有不相等的值,继续做迭代条件。这是尾上重复的情况,后面没有值了


            //极端情况2这里空就不要运行了
            if(next)
            {
    
    
                 next=next->next;
            }

完整代码:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
    
    
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
    
    
      if(pHead==NULL||pHead->next==NULL)
          return pHead;
        ListNode* prev=NULL;
        ListNode* cur=pHead;
        ListNode* next=cur->next;
        while(next)
        {
    
    
            //不相等同时向后走
        if(cur->val!=next->val)
        {
    
    
            prev=cur;
            cur=next;
            next=next->next;
        }
            else{
    
    
                //写成if语句只走了一下
                //相等让cur停下来,next走到一个不相等的地方,那假如后面一直相等是不是一直走
                //prev把他自己和next连起来
                //free中间的节点,这个时候时候cur走到了next的地方
               //让next移动到下一个位置
                
                //假如是尾上重复,因为是再循环里,next走到停不下来,我们让他NULL时候不进while,就停下了
                while(next && cur->val == next->val)
                {
    
    
                    
                //尾上重复的情况,最后一次进循环这里是空
                    next=next->next;
                }
                   
                
                
                if(prev){
    
    
                prev->next=next;}
                //这是极端情况1,假如头结点重复导致走到这里prev还是空
                else{
    
    
                    //让next这个位置成为头
                   pHead=next;
                }
                //释放跳过的节点,cur此时也在走
                while(cur!=next)
                {
    
    
                    //每次循环完都等于下一个节点
                    ListNode* del=cur;
                    cur=cur->next;
                    free(del);
                }
            //尾上重复的那种情况,因为上面进行过一次next=next->next他已经是空了
            //极端情况2这里空就不要运行了
            if(next)
            {
    
    
                 next=next->next;
            }
            }
        }
        return pHead;
        
    }
};

最后简单总结一下,代码不是很难,只要把逻辑想清楚就可以一步一步写出来,但是对于链表,我们要有这种思想,先写出自己的逻辑,然后判断极端情况,而这种极端情况大部分都是由头和尾异常引起的,我们多多思考这种测试用例进行画图,然后才是编写代码。

猜你喜欢

转载自blog.csdn.net/qq_45928272/article/details/113440997