删除排序链表中的重复节点

本文探讨如何删除排序链表中的重复元素,保持每个元素只出现一次。首先提出基础问题,通过比较相邻节点实现去重,时间复杂度为O(N),空间复杂度为O(1)。然后讨论更复杂的情况,引入窗口滑动思想,实现删除所有重复数字的节点,时间复杂度仍为O(N),但空间复杂度可能增加到O(N)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.基础问题
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 :
输入: 1->1->2
输出: 1->2
2.算法
这里起初想法就是想着两个节点两个节点的比较以上述例子为例子。首先两个指针P和Q指向两个1,比较两个指针的值,发现相同;
在这里插入图片描述
此时将Q所在的这一个节点断开,P指向的节点不变,将Q指向下一节点继续比较:
在这里插入图片描述
直至Q指向链表末端:
在这里插入图片描述

/**
 * 链表节点定义
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution83 {
	public ListNode deleteDuplicates(ListNode head) {
		//边界条件
		if(head==null ||head.next==null)
			return head;
		
		ListNode temp=head.next,pre=head;//前后节点
		while(temp!=null) {
		//前后节点进行判断,如果相等,则进行链表重链接操作
			if(temp.val==pre.val) {	
				pre.next=temp.next;
				temp.next=null;
				temp=pre.next;
			}
			else {
				temp=temp.next;
				pre=pre.next;
			}
		}	
		return head;
    }
}

现在这个比较简单,时间复杂度为O(N),空间复杂度为O(1),没有使用其他的空间。
3.问题升级
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
示例 1:
输入: 1->2->3->3->4->4->5
输出: 1->2->5
4.算法
在解决这个问题的时候,我就有了窗口滑动的想法,具体解释如下:
初始情况链表如下:
在这里插入图片描述
为了画图快点,节点之间的链接就没有画出来,Num是一个记录值,具体记录看步骤解释就知道了。
第一步:
比较P和Q,即窗口大小为2,当P和Q不相同时,Num=P.val=1;然后P和Q指向下一节点。(一直到P和Q的值相同的情形)
在这里插入图片描述
第二步:
此时P和Q相同,因此先删除P所在节点,然后P和Q一样指向下一节点。
相同时情况:
在这里插入图片描述
断开P现在节点,然后P,Q指向下一节点:(黄色节点表示这个节点已断开)
在这里插入图片描述
第三步:
这步是整个算法的判断核心,也是Num其作用的一步,即此时P和Q不同,但是根据题目要求来说,P现在所在的节点在上一步就应该删除掉,因此需要判断当P!=Q时,若P=Num,则此时的P也得断开。断开后,P和Q继续指向下一节点。
在这里插入图片描述
接着一直重复上述操作,直到Q指向链表末端。
代码如下:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null ||head.next==null)
			return head;
		ListNode temp=head.next,next=head,newlist= new ListNode(0),newtemp=newlist;
		int num=Integer.MIN_VALUE;

		while(temp!=null) {
			if(temp.val==next.val) {	//滑动一个节点
				num=next.val;
				temp=temp.next;
				next=next.next;
                continue;
			}
			else if(temp.val!=next.val) {
			//判断是否与上一个窗口的最后一个链表节点相同
				if(next.val!=num) {	
					newtemp.next=next;
					newtemp=newtemp.next;

					num=next.val;   //滑动窗口
					next=next.next;
					temp=temp.next;

                    newtemp.next=null;//链表断开,否则链接会错误
				}
				else {
					next=temp;	//滑动窗口
					temp=temp.next;
				}
			}
		}
        if(temp==null&&next!=null) {
        	//判断是否与上一个窗口的最后一个链表节点相同
			if(next.val!=num) {
				newtemp.next=next;
				newtemp=newtemp.next;
				newtemp.next=null;
		}
		}
        return newlist.next;
	}
		
}

这样的算法时间复杂度也还是O(N),空间复杂度最大为O(N),最小为O(1),我这里是用了一个新的链表来链接节点,因此空间复杂度会大一点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值