牛客题霸面试真题——链表:NC25 删除有序链表中重复的元素-I、NC24 删除有序链表中重复的元素-II

(1)删除链表中的节点

在这里插入图片描述
pre初始化为虚拟头结点,cur初始化为pHead,遍历链表时如果查到该节点,pre->next=cur->next;

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @param val int整型 
     * @return ListNode类
     */
    ListNode* deleteNode(ListNode* head, int val) {
        // write code here
        ListNode *fakeHead= new ListNode(-1);
        fakeHead->next=head;
        ListNode *pre=fakeHead;
        ListNode *cur=pre->next;
        while(cur){
            if(cur->val==val){
                pre->next=cur->next;
                break;
            }
            pre=pre->next;
            cur=cur->next;
        }
        return fakeHead->next;   
    }
};

(2)NC25 删除有序链表中重复的元素-I

原题链接:https://www.nowcoder.com/practice/c087914fae584da886a0091e877f2c79
在这里插入图片描述
在这里插入图片描述

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* deleteDuplicates(ListNode* head) {
        // write code here
        //链表为空直接返回
        if(head==nullptr) return head;
        ListNode *p=head;
        while(p->next!=nullptr){
            if(p->val==p->next->val) p->next=p->next->next;
            else p=p->next;
        }
        return head;
    }
};

(3)NC24 删除有序链表中重复的元素-II

原题链接:https://www.nowcoder.com/practice/71cef9f8b5564579bf7ed93fbe0b2024
在这里插入图片描述
与上题不同之处在于:上一题要如果出现重复的元素那么只留一个,此题如果出现重复的元素那么一个都不留,要删除所有重复的元素

解法一(双指针):

在这里插入图片描述
在这里插入图片描述
我们已经用newhead来帮助我们解决了第一个元素是重复元素的问题,还没有解决最后一个节点是重复节点的问题。

在此做以解释:因为每次我们都要用cur->val与cur->next->val进行比较,所以cur->next不能为空,所以while循环的条件里是while(cur&&cur->next)。但如果最后一个元素重复即cur->val=cur->next->val(如图中的…->3->3->null),在此情况下按照上图的步骤,我们只修改了cur的next指针为cur->next->next(如上图中⑤的第一个图),在下一次循环中才会对pre的next指针进行修改,但此时已经跳出循环!所以这种情况要单独对最后一个pre->next进行赋值

这里一定要明确pre指针、cur指针的意义:

  • pre记录上一步中的非重复元素,即会出现在最终结果中的节点
  • cur则计算pre的next指针应指向哪一个元素(即cur用来判断当前节点是否应当被加入答案链表中)

当前节点=下一个节点值时,更新cur的next指针判断后面是否也是重复的节点

  • …2(pre)->3(cur)->3->3->null,修改cur的next指针cur->next=cur->next->next;,变为…2(pre)->3(cur)->3->null(虽然还存在重复节点,但是会在下一次循环中进行修改)

当前节点≠下一个节点值时,有两种情况:

  • cur此时是重复节点: …2(pre)->3->3(cur)->4->null,要完全删除重复节点即pre->next=cur->next
  • cur此时不是重复节点:…2->3->3(pre)->4(cur)->null,将cur加入答案链表即pre=cur

这样不断地通过pre找到非重复元素,经过cur的连接就构成了新的结果链表,由于我们新建了一个newhead作为虚拟的头节点,所以结果链表的头节点变为newhead->next。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* deleteDuplicates(ListNode* head) {
        // write code here
        ListNode *fakehead = new ListNode(-1);
        fakehead ->next=head;
        ListNode *pre=fakehead;
        ListNode *cur=pre->next;
        int count=0;
        while(cur&&cur->next){
            //判断当前节点cur和下一个节点是否相同,则进行处理:跳过下一个节点进行连接
            if(cur->val==cur->next->val){
                cur->next=cur->next->next;
                //标记cur为重复节点
                count++;
            }else{//cur的值和下一个节点不同,则判断cur是否应被加入答案
                //count>0说明cur为重复节点,应删除cur
                if(count>0) {
                    pre->next=cur->next;//pre->next要跳过cur
                    count=0;
                }
                //cur不是重复节点,则将pre顺次后移(将cur放入答案链表中)
                else pre=cur;
                //无论哪种情况,只要当前和下一个不相等,cur就后移更新
                cur=cur->next;
            }
        }
        //当最后一个点是重复节点,给pre->next赋值
        if(count>=0) pre->next=cur->next;
        return fakehead->next;
    }
};

注:这里的特殊情况就是head=nullptr,由于我们设置了newhead->next=head,所以newhead->next = nullptr,此情况不会进入任何一个程序段会直接返回newhead->next即nullptr。所以无需单独提出来进行处理

解法二(递归):

当head后面存在值且与head的值相等的话,那么就一直向后找,直到找到不相等的值为止,此时跳出查找的循环,得到一个非重复的元素。对于后面的结点也是应该进行这样的操作,即循环向后遍历直到找到一个非重复的元素。这个过程都是相似的所以可以进行递归操作:如果head的值与head->next的值不相等的话,那么就递归后面的结点,再使用head->next = deleteDuplicates(head->next);的操作将下层返回的节点进行连接,最后返回第一层的head即可。图示如下:
在这里插入图片描述

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* deleteDuplicates(ListNode* head) {
        // write code here
        if(head==nullptr) return head; //递归结束条件
        if(head->next!=nullptr && head->val == head->next->val){
            while(head->next!=nullptr && head->val == head->next->val)
                head = head->next;
            return deleteDuplicates(head->next);
        }
        head->next = deleteDuplicates(head->next);
        return head;
    }
};

两种方法的对比:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值