返回

揭秘解题套路:手把手教你攻克单链表六大难题

闲谈

破解单链表六大解题套路:助你轻松驾驭各种难题

身为一名程序员,你是否曾被单链表的复杂问题困扰不已?比如合并有序链表、寻找环形链表、确定链表中点等。这些难题看似棘手,但掌握正确的解题套路,它们将迎刃而解。

1. 合并两个有序链表

想象你有一对井然有序的链表,你需要将它们合并成一个新的有序链表。套路很简单:

  • 定义两个指针,分别指向两个链表的头部。
  • 比较两个指针所指向节点的值。
  • 如果值相等,合并节点,并将指针指向下一个节点。
  • 如果值不等,将较小节点合并到较大节点后,并将较小节点的指针指向较大节点的下一个节点。
  • 重复以上步骤,直到两个指针都指向空节点。

代码示例:

def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
    dummy = ListNode(0)
    curr = dummy
    while l1 and l2:
        if l1.val < l2.val:
            curr.next = l1
            l1 = l1.next
        else:
            curr.next = l2
            l2 = l2.next
        curr = curr.next
    curr.next = l1 or l2
    return dummy.next

2. 合并 K 个升序链表

这是一个合并有序链表的高级版,你需要将 K 个有序链表合并为一个。套路与上一条相似:

  • 定义一个虚拟节点,并将 K 个链表的头部指向它。
  • 比较虚拟节点的下一个节点的值。
  • 如果值相等,合并节点,并将虚拟节点的下一个节点指向下一个节点。
  • 如果值不等,将较小节点合并到较大节点后,并将较小节点的指针指向较大节点的下一个节点。
  • 重复以上步骤,直到虚拟节点的下一个节点指向空节点。

代码示例:

def mergeKLists(self, lists: List[ListNode]) -> ListNode:
    dummy = ListNode(0)
    curr = dummy
    heap = []
    for node in lists:
        if node:
            heapq.heappush(heap, (node.val, node))

    while heap:
        val, node = heapq.heappop(heap)
        curr.next = node
        curr = curr.next
        if node.next:
            heapq.heappush(heap, (node.next.val, node.next))

    return dummy.next

3. 寻找环形链表

环形链表是指链表中的某个节点指向链表中的另一个节点,形成一个环。套路如下:

  • 使用快慢指针。
  • 快指针每次移动两个节点,慢指针每次移动一个节点。
  • 如果快指针和慢指针相遇,则链表中存在环。

代码示例:

def hasCycle(self, head: ListNode) -> bool:
    if not head or not head.next:
        return False

    slow = head
    fast = head.next

    while slow != fast:
        if not fast or not fast.next:
            return False
        slow = slow.next
        fast = fast.next.next

    return True

4. 寻找环形链表 II

这是寻找环形链表的进阶版,你需要找到环的入口节点。套路如下:

  • 使用两个指针,一个指向链表的头部,另一个指向相遇的两个节点。
  • 将指向头部 的指针每次移动一步,将指向相遇节点的指针每次移动两步。
  • 当两个指针相遇时,相遇的节点就是环的入口节点。

代码示例:

def detectCycle(self, head: ListNode) -> ListNode:
    if not head or not head.next:
        return None

    slow = head
    fast = head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

        if slow == fast:
            break

    if slow != fast:
        return None

    slow = head

    while slow != fast:
        slow = slow.next
        fast = fast.next

    return slow

5. 寻找链表的中点

你需要找到链表中点的节点。套路如下:

  • 使用快慢指针。
  • 快指针每次移动两个节点,慢指针每次移动一个节点。
  • 当快指针到达链表的尾部时,慢指针刚好到达链表的中点。

代码示例:

def middleNode(self, head: ListNode) -> ListNode:
    slow = head
    fast = head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

    return slow

6. 反转链表

你需要将链表中的所有节点的顺序颠倒过来。套路如下:

  • 使用三个指针,分别指向当前节点、前一个节点和下一个节点。
  • 将当前节点的下一个节点指向前一个节点,并将前一个节点指向当前节点。
  • 重复以上步骤,直到当前节点指向空节点。

代码示例:

def reverseList(self, head: ListNode) -> ListNode:
    prev = None
    curr = head

    while curr:
        next_node = curr.next
        curr.next = prev
        prev = curr
        curr = next_node

    return prev

总结

掌握这六大解题套路,你将能够轻松应对各种单链表问题。

常见问题解答

  • 为什么使用快慢指针来查找环形链表?

    • 快慢指针可以帮助确定链表中是否存在环,因为如果存在环,快指针最终将追上慢指针。
  • 如何找到环形链表的入口节点?

    • 在发现环形链表后,可以将一个指针指向头部,另一个指针指向相遇的节点,然后以相同的速度移动两个指针,相遇的节点就是入口节点。
  • 为什么反转链表需要三个指针?

    • 三个指针用于跟踪当前节点、前一个节点和下一个节点,以正确更新链表的指针。
  • 能否只使用一个指针来反转链表?

    • 不可以,因为只使用一个指针无法存储前一个节点的信息。
  • 如何用单链表实现队列?

    • 可以使用两个指针,一个指向队头,一个指向队尾,并通过尾部指针插入元素,通过头部指针删除元素。