链表去重:如何精准删除排序链表中的重复元素?
在算法世界里,链表是一种常见的数据结构,它灵活、动态,适合插入和删除操作。然而,处理排序链表中的重复元素,却是一个略显棘手的问题——既要确保删除掉所有重复值,又要维持链表的整体结构。这就涉及到了条件删除的核心逻辑。今天,我们就来拆解**“删除排序链表中的重复元素 II”**这个问题,看看如何高效、精准地实现它。
1. 题目解析:究竟该删什么?
题目要求:
- 给定一个 升序排序 的链表。
- 需要删除所有重复出现的元素,仅保留未重复的元素。
示例:
输入: 1 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5
输出: 1 -> 2 -> 5
可以看到,所有重复的 3 和 4 都被清理掉,而非简单地删除重复项后保留一个。这意味着,我们必须精准识别和剔除重复值。
2. 解决思路:双指针遍历链表
由于链表已经有序,我们可以利用双指针遍历的方式:
-
使用一个虚拟头结点(dummy),方便处理链表头部的删除操作。
-
采用前置指针(prev)和当前指针(current):
prev
指向上一层有效节点;current
负责扫描当前元素,检测是否有重复。
-
发现重复元素时,跳过整个重复区间:
- 如果
current
指向的元素有多个相邻重复值,则prev
直接跳过这些值,指向下一个不同的元素。 - 直到遍历结束,返回新的链表。
- 如果
3. 代码实现:Python版
让我们用 Python 实现这个逻辑:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def deleteDuplicates(head):
"""
删除排序链表中的重复元素(只保留未重复的元素)
"""
dummy = ListNode(0) # 虚拟头结点,方便处理头部删除问题
dummy.next = head
prev = dummy # 前置指针
current = head # 当前指针
while current:
# 检测是否有重复元素
if current.next and current.val == current.next.val:
# 跳过所有重复的元素
while current.next and current.val == current.next.val:
current = current.next
prev.next = current.next # 彻底跳过该元素
else:
prev = prev.next # 正常移动前置指针
current = current.next # 继续遍历
return dummy.next # 返回处理后的链表
代码解析:
- 虚拟头结点
dummy
:避免删除头部元素时的特殊处理。 prev.next = current.next
:当current
指向重复元素时,跳过整个区间。- 外层
while current
:确保链表遍历到底。
4. 代码示例运行
假设我们的输入链表是:
1 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5
用下面的方式测试代码:
# 构建测试链表
head = ListNode(1, ListNode(2, ListNode(3, ListNode(3, ListNode(4, ListNode(4, ListNode(5)))))))
# 运行去重函数
result = deleteDuplicates(head)
# 打印结果
while result:
print(result.val, end=" -> ")
result = result.next
输出结果:
1 -> 2 -> 5 ->
成功删除所有重复值,仅保留唯一的数值。
5. 复杂度分析:性能如何?
- 时间复杂度:O(n),我们只需遍历链表一次。
- 空间复杂度:O(1),不需要额外的存储,只使用几个辅助指针。
这意味着,该方法在大规模链表数据下依然高效,不会占用太多资源。
6. 扩展优化:如何处理未排序链表?
如果链表是无序的,那么上述方法就不适用了。我们可以先对链表排序,再采用相同的逻辑删除重复元素:
def sort_linked_list(head):
"""
对链表排序(使用归并排序)
"""
if not head or not head.next:
return head
# 快慢指针拆分链表
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
mid = slow.next
slow.next = None
# 递归排序
left = sort_linked_list(head)
right = sort_linked_list(mid)
return merge_sorted_list(left, right)
def merge_sorted_list(l1, l2):
"""
归并两个已排序链表
"""
dummy = ListNode(0)
tail = dummy
while l1 and l2:
if l1.val < l2.val:
tail.next, l1 = l1, l1.next
else:
tail.next, l2 = l2, l2.next
tail = tail.next
tail.next = l1 or l2
return dummy.next
先排序,再用双指针删除重复值,这样就能兼容无序链表。
7. 总结
🔹 核心思路:利用双指针遍历,精准跳过重复区间。
🔹 代码实现:使用 dummy
虚拟头结点,确保边界处理统一。
🔹 性能分析:O(n) 时间复杂度 + O(1) 空间复杂度,高效稳定。
🔹 扩展优化:如果链表是无序的,先排序再删除重复元素。