链表操作是数据结构与算法面试中的高频考点,其中回文链表和相交链表这两类问题更是各大技术面试的"常客"。作为面试官,我见过太多候选人在这些问题上栽跟头——不是解法时间复杂度不达标,就是代码冗长难以维护。今天我就来分享这两个问题的黄金解法模板,这些方法都是我在实际面试评判和代码评审中验证过的最优方案。
回文链表(LeetCode 234)要求判断一个单链表是否为回文结构,最优解法需要达到O(n)时间复杂度和O(1)空间复杂度。相交链表(LeetCode 160)则需要找出两个链表的相交节点,同样有严格的空间复杂度限制。这两个问题看似简单,但要写出既高效又优雅的代码,需要掌握链表操作的核心技巧。
新手最常见的思路是将链表值复制到数组,然后用双指针判断回文。这种方法虽然直观,但需要O(n)额外空间:
python复制def isPalindrome(head):
vals = []
while head:
vals.append(head.val)
head = head.next
return vals == vals[::-1]
面试官期待的解法应该在不使用额外空间的情况下完成判断。这就需要我们掌握链表的"原地修改"技巧。
最优解法的核心在于:
python复制def isPalindrome(head):
if not head or not head.next:
return True
# 找中点
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 反转后半部分
prev = None
while slow:
nxt = slow.next
slow.next = prev
prev = slow
slow = nxt
# 比较前后两部分
left, right = head, prev
while right: # 后半部分可能比前半部分短1个节点
if left.val != right.val:
return False
left = left.next
right = right.next
return True
注意:在实际面试中,如果面试官要求保持链表原状,还需要添加链表恢复的代码。这是一个常被忽略但很重要的细节。
时间复杂度:O(n) —— 三次线性遍历
空间复杂度:O(1) —— 只用了几个指针变量
边界情况处理:
最直观的方法是使用哈希表存储一个链表的所有节点,然后遍历另一个链表查找相交点:
python复制def getIntersectionNode(headA, headB):
nodes = set()
while headA:
nodes.add(headA)
headA = headA.next
while headB:
if headB in nodes:
return headB
headB = headB.next
return None
这种方法虽然简单,但需要O(n)额外空间,不符合最优解要求。
最优解法利用了一个精妙的数学原理:两个指针分别遍历两个链表,当到达末尾时切换到另一个链表头部继续遍历,最终会在相交点相遇。
python复制def getIntersectionNode(headA, headB):
pA, pB = headA, headB
while pA != pB:
pA = pA.next if pA else headB
pB = pB.next if pB else headA
return pA
这个解法之所以有效,是因为:
时间复杂度:O(m+n) —— 最多遍历两遍两个链表
空间复杂度:O(1) —— 只用了两个指针
正确性证明:
设链表A独有部分长度为a,链表B独有部分长度为b,公共部分长度为c。
python复制def isPalindrome_template(head):
# 边界判断
if not head or not head.next:
return True
# 找中点(模板)
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 反转后半部分(模板)
prev = None
while slow:
nxt = slow.next
slow.next = prev
prev = slow
slow = nxt
# 业务逻辑:比较前后部分
left, right = head, prev
result = True
while right:
if left.val != right.val:
result = False
break
left = left.next
right = right.next
# 恢复链表(可选)
# ...
return result
python复制def getIntersectionNode_template(headA, headB):
# 初始化双指针
pA, pB = headA, headB
# 双指针走位(模板)
while pA != pB:
pA = pA.next if pA else headB
pB = pB.next if pB else headA
# 返回相遇点(可能是None)
return pA
忽略链表恢复:某些面试场景会要求保持链表原状
中点定位错误:快慢指针实现有误导致中点定位不准
边界条件遗漏:空链表或单节点链表情况
死循环风险:未正确处理不相交的情况
长度计算误区:试图先计算链表长度再处理
节点比较错误:比较节点值而非节点本身
掌握了这两个模板后,可以解决许多变种问题:
我在技术面试中经常看到候选人能写出基本解法,但在以下方面失分:
记住:面试不仅是考察算法能力,更是考察工程实践和沟通能力。即使知道最优解法,如果不能清晰地表达和实现,也会影响最终评价。