链表去重是数据结构基础操作中的经典问题,也是技术面试中的高频考点。以LeetCode第83题为例,题目要求在一个已排序的链表中,删除所有重复元素,使得每个元素只出现一次。这个问题看似简单,却涵盖了链表操作的多个核心知识点。
在实际开发中,处理有序数据去重的场景非常普遍。比如日志系统中去除连续的重复记录、用户行为分析时合并相同事件等。掌握链表去重的技巧,不仅能帮助我们高效解决这类问题,更能深入理解链表这一基础数据结构的操作特性。
链表由节点通过指针连接而成,每个节点包含数据域和指针域。与数组不同,链表在内存中不是连续存储的,这使得它的插入和删除操作时间复杂度为O(1),但随机访问效率较低。
在本题中,链表已经按升序排列,这意味着所有重复元素必定相邻。这个特性大大简化了问题,我们只需要比较相邻节点即可发现重复。
最常见的解法是使用双指针技巧:
当fast指针发现新元素时,将slow指针的next指向该元素,然后移动slow指针。这种解法只需要一次遍历,时间复杂度O(n),空间复杂度O(1),是最优解。
python复制class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def deleteDuplicates(head: ListNode) -> ListNode:
if not head:
return head
slow = head
fast = head.next
while fast:
if slow.val != fast.val:
slow.next = fast
slow = slow.next
fast = fast.next
slow.next = None # 切断后续可能存在的重复节点
return head
注意:最后的slow.next = None操作很容易被忽略,但在链表末尾有重复元素时必不可少。
算法只对链表进行一次遍历,时间复杂度为O(n),n为链表长度。这是最优时间复杂度,因为必须检查每个节点。
只使用了常数级别的额外空间(两个指针变量),空间复杂度为O(1)。对于大规模数据处理非常友好。
可以进一步简化代码,只使用一个current指针:
python复制def deleteDuplicates(head: ListNode) -> ListNode:
current = head
while current and current.next:
if current.val == current.next.val:
current.next = current.next.next
else:
current = current.next
return head
这种写法更简洁,但原理相同。在实际面试中,建议先写出完整版本确保正确性,再考虑优化。
在处理有序的数据库查询结果时,类似的算法可用于去除相邻重复记录,比使用DISTINCT关键字更高效。
在分析服务器日志时,连续出现的相同错误信息可以通过这种算法合并,减少存储和分析开销。
链表操作是面试中的基础考核点,建议熟练掌握各种指针操作技巧。在实际编码时,我习惯先用注释写出算法步骤,再填充代码,这样可以减少逻辑错误。对于这道题,关键是要理解双指针的运动规律,以及在什么条件下需要更新指针关系。