Python面试宝典第45题:链表的中间节点

69 阅读4分钟

题目

给你单链表的头结点head ,请找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:head = [1, 2, 3, 4, 5]
输出:3
解释:该链表只有一个中间结点,值为3

示例 2:

输入:head = [1, 2, 3, 4, 5, 6]
输出:4
解释:该链表有两个中间结点,值分别为34,返回第二个结点。

迭代法

由于链表没有索引,无法随机访问,故我们需要先遍历整个链表来计算链表的长度,然后再次遍历链表直到到达中间位置。使用迭代法求解本题的主要步骤如下。

1、初始化一个变量length为0。

2、使用一个指针current遍历整个链表,每遍历一个节点,length加1。

3、计算中间位置middle,如果是奇数长度,中间位置为:length//2。如果是偶数长度,中间位置为:length // 2 + 1。

4、再次遍历链表,直到到达中间位置,并返回中间节点。

根据上面的算法步骤,我们可以得出下面的示例代码。

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

# 构建链表
def build_linked_list(values):
    if not values:
        return None
    head = ListNode(values[0])
    current = head
    for value in values[1:]:
        current.next = ListNode(value)
        current = current.next
    return head

def middle_node_of_linked_list_by_iteration(head):
    # 计算链表长度
    length = 0
    current = head
    while current:
        length += 1
        current = current.next
    
    # 计算中间位置
    middle = length // 2

    # 再次遍历链表直到到达中间位置
    current = head
    for _ in range(middle):
        current = current.next
    
    return current

values = [1, 2, 3, 4, 5]
head = build_linked_list(values)
print(middle_node_of_linked_list_by_iteration(head).val)

values = [1, 2, 3, 4, 5, 6]
head = build_linked_list(values)
print(middle_node_of_linked_list_by_iteration(head).val)

转换数组法

转换数组法的基本思想是:先将链表转换为数组,然后根据数组的长度来计算中间位置,最后返回中间位置的节点。使用转换数组法求解本题的主要步骤如下。

1、初始化一个空列表nodes。

2、使用一个指针current遍历整个链表,将每个节点添加到nodes列表中。

3、计算中间位置middle,并返回nodes[middle]。

根据上面的算法步骤,我们可以得出下面的示例代码。

def middle_node_of_linked_list_by_array(head):
    # 将链表转换为数组
    nodes = []
    current = head
    while current:
        nodes.append(current)
        current = current.next
    
    # 计算中间位置
    middle = len(nodes) // 2
    
    # 返回中间节点
    return nodes[middle]

values = [1, 2, 3, 4, 5]
head = build_linked_list(values)
print(middle_node_of_linked_list_by_array(head).val)

values = [1, 2, 3, 4, 5, 6]
head = build_linked_list(values)
print(middle_node_of_linked_list_by_array(head).val)

快慢指针法

快慢指针法的基本思想是:使用两个指针,一个快指针每次移动两步,一个慢指针每次移动一步;当快指针到达链表尾部时,慢指针刚好指向中间节点。使用快慢指针法求解本题的主要步骤如下。

1、初始化两个指针slow和fast,它们都指向链表的头部。

2、在每次迭代中,slow指针向前移动一步,fast指针向前移动两步。

3、当fast指针到达链表尾部时,slow指针正好指向中间节点。

4、返回slow指针所指向的节点。

根据上面的算法步骤,我们可以得出下面的示例代码。

def middle_node_of_linked_list_by_fast_slow_pointer(head):
    slow = head
    fast = head
    
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    
    return slow

values = [1, 2, 3, 4, 5]
head = build_linked_list(values)
print(middle_node_of_linked_list_by_fast_slow_pointer(head).val)

values = [1, 2, 3, 4, 5, 6]
head = build_linked_list(values)
print(middle_node_of_linked_list_by_fast_slow_pointer(head).val)

总结

迭代法需要遍历链表两次,故时间复杂度为O(n)。其空间复杂度为O(1),只使用了有限的额外空间。

转换数组法需要遍历链表一次,并将节点添加到数组中,故时间复杂度和空间复杂度均为O(n)。转换数组法可以方便地访问链表中的任意节点,但需要额外的空间来存储所有节点,空间复杂度较高。

快慢指针法需要遍历链表一次,故时间复杂度为O(n)。其空间复杂度为O(1),只使用了有限的额外空间。快慢指针法是求解此类题最常用且最高效的方法,实现简单直观,仅需遍历链表一次。