题目
给你单链表的头结点head ,请找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:head = [1, 2, 3, 4, 5]
输出:3
解释:该链表只有一个中间结点,值为3。
示例 2:
输入:head = [1, 2, 3, 4, 5, 6]
输出:4
解释:该链表有两个中间结点,值分别为3和4,返回第二个结点。
迭代法
由于链表没有索引,无法随机访问,故我们需要先遍历整个链表来计算链表的长度,然后再次遍历链表直到到达中间位置。使用迭代法求解本题的主要步骤如下。
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),只使用了有限的额外空间。快慢指针法是求解此类题最常用且最高效的方法,实现简单直观,仅需遍历链表一次。