100道面试必会算法-26-删除排序链表中的重复元素||
链表是一种常见的数据结构,它以节点的形式存储数据,每个节点包含数据以及指向下一个节点的引用。链表在插入和删除操作上有较高的效率,因此在许多应用中得到了广泛的使用。然而,当链表包含重复的元素时,如何有效地删除这些重复元素,成为了一个常见的编程问题。今天将探讨如何在一个已排序的链表中删除所有重复的数字,只留下不同的数字。
问题描述
给定一个已排序的链表的头节点 head
,要求删除链表中所有重复的节点,只留下所有不同的数字,并返回一个新的链表头节点。
示例 1:
输入: 1 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5
输出: 1 -> 2 -> 5
示例 2:
输入: 1 -> 1 -> 1 -> 2 -> 3
输出: 2 -> 3
解题思路
要解决这个问题,可以使用双指针方法。一个指针用于遍历链表,另一个指针用于构建新的链表。通过比较当前节点与下一个节点的值,可以确定是否存在重复的节点。如果存在重复,我们将跳过这些节点;如果不存在重复,将当前节点连接到新的链表中。
具体步骤
- 创建一个虚拟头节点(dummy node),这个节点的 next 指向链表的头节点。虚拟头节点有助于处理头节点本身被删除的情况。
- 使用两个指针
pre
和node
。pre
指向虚拟头节点,node
用于遍历链表。 - 遍历链表,当
node
和node.next
的值相等时,继续向后移动node
,直到遇到不同的值为止。然后将pre
的 next 指向node
的 next,以跳过重复节点。 - 如果
node
和node.next
的值不相等,直接移动pre
指针。 - 重复上述过程直到遍历完整个链表。
代码实现
下面是 Java 代码实现:
java
复制代码
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) {
// 创建一个虚拟头节点
ListNode hair = new ListNode(0, head);
// pre 指针指向虚拟头节点
ListNode pre = hair;
// node 指针用于遍历链表
ListNode node = head;
// 遍历链表
while (node != null && node.next != null) {
// 如果当前节点与下一个节点值相等
if (node.val == node.next.val) {
// 向后移动 node 指针,直到遇到不同的值
while (node.next != null && node.val == node.next.val)
node = node.next;
// 跳过重复节点
pre.next = node.next;
} else {
// 如果没有重复,移动 pre 指针
pre = pre.next;
}
// 继续向后移动 node 指针
node = node.next;
}
// 返回处理后的链表头节点
return hair.next;
}
}
代码解析
-
初始化虚拟头节点和指针:
ListNode hair = new ListNode(0, head); ListNode pre = hair; ListNode node = head;
-
遍历链表:
while (node != null && node.next != null) { if (node.val == node.next.val) { while (node.next != null && node.val == node.next.val) node = node.next; pre.next = node.next; } else { pre = pre.next; } node = node.next; }
-
返回新的链表头节点:
return hair.next;
总结
通过上述方法,可以有效地删除排序链表中的重复元素,只保留不重复的元素。该算法的时间复杂度为 O(n),空间复杂度为 O(1),因此在处理大型链表时也能表现出色。这一解法不仅适用于本题,还可以扩展应用于其他类似的链表去重问题中。希望这篇博客能帮助你更好地理解和解决链表去重的问题!