跳转至

442_链表面试题精选


一句话说明

链表面试题核心是指针操作,快慢指针解环检测/中点/倒数,反转链表是手写必考,做题前先画图,指针别乱飞。


核心知识点

白话理解

链表就是一串项链,每颗珠子(节点)知道下一颗在哪,但不知道上一颗。面试里操作链表就是"调整珠子间的绳子指向",最容易出错的是断链(修改指针前没保存引用)。

常用技巧

  1. 虚拟头节点(dummy head):避免处理头节点的特殊逻辑
  2. 快慢指针:快指针走两步,慢指针走一步,用于找中点、检测环
  3. 双指针:找倒数第 k 个节点
  4. 先画图:链表操作必须先画出节点关系图

经典题目与解法

题目1:反转链表(LeetCode 206,必考)

题意:反转一个单向链表,返回新的头节点。

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def reverse_list(head):
    """
    迭代法:三指针滚动
    prev → current → next_node
    每次把 current 的 next 指向 prev,然后三者都向前移一步
    """
    prev = None                            # 前驱节点(初始为None,即新链表尾部)
    curr = head                            # 当前节点

    while curr:                            # 遍历到链表尾部
        next_node = curr.next              # 先保存下一个节点(断链前必须保存!)
        curr.next = prev                   # 反转:当前节点指向前驱
        prev = curr                        # 前驱向前移
        curr = next_node                   # 当前节点向前移

    return prev                            # prev 就是新的头节点

# 递归法(理解递归非常有帮助)
def reverse_list_recursive(head):
    """
    递归:先递归到尾部,再从尾部反转回来
    """
    if not head or not head.next:          # 空链表或只有一个节点
        return head

    new_head = reverse_list_recursive(head.next)  # 先把后面全反转

    head.next.next = head                  # 让后一个节点指回当前节点
    head.next = None                       # 当前节点的next断开(变尾部)

    return new_head                        # 返回新头节点

# 辅助函数:构建和打印链表
def build_list(vals):
    dummy = ListNode(0)
    curr = dummy
    for v in vals:
        curr.next = ListNode(v)
        curr = curr.next
    return dummy.next

def print_list(head):
    result = []
    while head:
        result.append(head.val)
        head = head.next
    print(result)

# 测试
head = build_list([1, 2, 3, 4, 5])
print_list(reverse_list(head))    # [5, 4, 3, 2, 1]

时间复杂度:O(n)
空间复杂度:迭代 O(1),递归 O(n) 递归栈


题目2:环形链表检测(LeetCode 142,快慢指针)

题意:判断链表是否有环,如果有,返回环的入口节点。

def detect_cycle(head):
    """
    快慢指针:
    第一步:找到相遇点
    第二步:从相遇点和头节点同速出发,再次相遇处就是环入口

    数学推导:设链表头到环入口距离为a,环入口到相遇点为b,环长为c
    快指针走了 a+b+c,慢指针走了 a+b
    快=慢×2 → a+b+c = 2(a+b) → c-b = a
    所以从相遇点走a步 = 从头走a步,会在环入口相遇
    """
    slow = head                            # 慢指针:每次走1步
    fast = head                            # 快指针:每次走2步

    # 第一步:找相遇点(判断有环)
    while fast and fast.next:
        slow = slow.next                   # 慢指针走1步
        fast = fast.next.next              # 快指针走2步

        if slow == fast:                   # 相遇,说明有环
            # 第二步:找环入口
            ptr = head
            while ptr != slow:             # 两个指针同速走,相遇处是环入口
                ptr = ptr.next
                slow = slow.next
            return ptr                     # 返回环入口节点

    return None                            # 没有环

# 简化版:只判断有无环(不找入口)
def has_cycle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False

时间复杂度:O(n)
空间复杂度:O(1)


题目3:合并两个有序链表(LeetCode 21)

题意:将两个升序链表合并为一个升序链表。

def merge_two_lists(l1, l2):
    """
    虚拟头节点法:避免处理合并结果头节点的特殊情况
    逐个比较两链表当前节点,小的接到结果链表
    """
    dummy = ListNode(0)                    # 虚拟头节点,方便操作
    curr = dummy                           # 结果链表的构建指针

    while l1 and l2:                       # 两个链表都还有节点
        if l1.val <= l2.val:
            curr.next = l1                 # 接l1的节点
            l1 = l1.next                   # l1向前移
        else:
            curr.next = l2                 # 接l2的节点
            l2 = l2.next                   # l2向前移
        curr = curr.next                   # 结果指针向前移

    curr.next = l1 if l1 else l2           # 接上剩余部分(直接接,不用逐个复制)

    return dummy.next                      # 返回真正的头节点

# 测试
l1 = build_list([1, 2, 4])
l2 = build_list([1, 3, 4])
print_list(merge_two_lists(l1, l2))  # [1, 1, 2, 3, 4, 4]

时间复杂度:O(m+n) m、n 是两链表长度
空间复杂度:O(1)


面试技巧

  1. 画图是第一步:拿到链表题必须先画出节点关系,避免逻辑错误
  2. 断链前先保存next_node = curr.next 这行必须在修改 curr.next 之前
  3. dummy head 减少边界判断:头节点可能被删除/修改时,加虚拟头节点
  4. 快慢指针口诀:找中点、找环、找倒数第k个——记住这三个场景
  5. 边界条件必检查:空链表、单节点、双节点——这三种情况必须验证

速查表

# 链表模板
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# 快慢指针找中点
slow = fast = head
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next
# slow 就是中点(偶数时是前半段最后一个)

# 虚拟头节点模板
dummy = ListNode(0)
dummy.next = head
# 操作结束后 return dummy.next

# 找倒数第k个节点
fast = slow = head
for _ in range(k):        # fast先走k步
    fast = fast.next
while fast:               # 再同步走,fast到尾时slow在倒数第k
    slow = slow.next
    fast = fast.next
题型技巧关键代码
反转三指针滚动prev, curr, next_node
有无环快慢指针slow×1, fast×2
找中点快慢指针fast到头时slow在中
找倒数k快慢指针差k步fast先走k步
合并有序虚拟头+逐一比较dummy + while l1 and l2