关注
文末的名片达文汐
,回复关键词“力扣源码”,即可获取完整源码!!详见:源码和核心代码的区别
题目详情
给定一个已排序的链表的头 head
,删除原始链表中所有重复数字的节点,只留下不同的数字。返回已排序的链表。
示例 1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3]
输出:[2,3]
提示:
- 链表中节点数目在范围
[0, 300]
内 -100 <= Node.val <= 100
- 题目数据保证链表已经按升序排列
解题思路
为了高效删除链表中所有重复的节点,我们可以利用链表已排序的特性。重复的节点在链表中一定是连续出现的。核心思路是使用两个指针:
- 哑节点(Dummy Node):简化头节点可能被删除的情况。
- 前驱指针(pre):指向当前已确认无重复的最后一个节点。
- 当前指针(cur):遍历链表,检查当前节点是否重复。
具体步骤:
- 初始化:创建哑节点
dummy
,其next
指向头节点head
。初始化pre = dummy
,cur = head
。 - 遍历链表:
- 当前节点与下一节点值相同:记录重复值
x
,循环跳过所有值为x
的节点。更新pre.next
指向跳过重复节点后的第一个节点。 - 当前节点与下一节点值不同:移动
pre
到cur
,表示当前节点无重复。
- 当前节点与下一节点值相同:记录重复值
- 移动当前指针:无论是否跳过重复节点,
cur
都会移动到下一个待检查节点。 - 返回结果:返回
dummy.next
,即新链表的头节点。
此方法确保每个节点只被访问一次,时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)。
代码实现(Java版)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null) return null;
ListNode dummy = new ListNode(0, head); // 哑节点,指向头节点
ListNode pre = dummy; // 前驱指针
ListNode cur = head; // 当前指针
while (cur != null) {
// 检查当前节点和下一节点是否重复
if (cur.next != null && cur.val == cur.next.val) {
int x = cur.val; // 记录重复值
// 跳过所有重复节点
while (cur != null && cur.val == x) {
cur = cur.next;
}
pre.next = cur; // 更新前驱指针的next
} else {
// 无重复,移动前驱指针
pre = cur;
cur = cur.next;
}
}
return dummy.next; // 返回新链表头节点
}
}
代码说明
- 哑节点(Dummy Node):用于处理头节点可能被删除的情况,确保链表操作统一。
- 前驱指针(pre):始终指向已确认无重复的最后一个节点。
- 当前指针(cur):遍历链表,若
cur
与cur.next
值相同,则循环跳过所有重复节点,并更新pre.next
。 - 非重复节点:直接移动
pre
到cur
,再后移cur
。 - 终止条件:当
cur
遍历完链表(cur == null
)时结束。
代码高效的关键在于:
- 利用哑节点统一处理逻辑。
- 每个节点只被访问一次,无额外空间开销。
- 精准跳过重复段,直接链接到非重复节点。