前言
快慢指针的使用是本节的核心。
我们有一个链表[1,2,3,4,5],如何找到中间的那个节点呢?
我们可以设计一个快指针以及一个慢指针,初始的时候都赋值为链表的起点,快指针每次走两步,慢指针每次走一步。 那么当快指针走到链表的末尾时,慢指针就正好停在了中间的位置。
以下是今天的例题
1. Leetcode 876. 链表的中间结点 https://leetcode.cn/problems/middle-of-the-linked-list/solution/mei-xiang-ming-bai-yi-ge-shi-pin-jiang-t-wzwm/
解题思路:
只要应用上面的思路,就可以轻松的写出这道题的代码。
我们只需要考虑fast节点是否可以往前走,以此来作为循环的终止条件即可。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
return slow
2. Leetcode141. 环形链表 https://leetcode.cn/problems/linked-list-cycle/solution/mei-xiang-ming-bai-yi-ge-shi-pin-jiang-t-c4sw/
解题思路:
fast和slow好像两个人在操场跑路,fast每次跑2步,slow每次跑1步。 那么fast每次比slow多跑1步,那么就是以1的相对速度再去追slow。 所以如果存在环的话,他们早晚会遇到。以下是代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
fast = slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast is slow:
return True
return False
3. Leetcode 142. 环形链表 II https://leetcode.cn/problems/linked-list-cycle-ii/solution/mei-xiang-ming-bai-yi-ge-shi-pin-jiang-t-nvsq/
解题思路:
数学题。设 a为 链表起点到环入口点的距离,b为a到slow和fast相遇点的距离,c为相遇点到环出口的距离。
那么有
fast = a+b + k(b+c)
slow = a+b, 慢指针永远不可能走完一圈(环),最坏情况slow在环的入口,fast在他前面一个节点的位置。 fast以相对速度1去追slow,最多走n-1步就可以和slow相遇。n为环长。
b+c = 环长
fast = 2 slow
----> a = c + (k-1)(b+c)
我们要求的是a,所以只要当fast和slow第一次相遇时(head从起点开始走,slow从b开始走),两者终会相遇(slow可能绕上几圈 再走上c步即可)。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
while True:
if head == slow:
return slow
head = head.next
slow = slow.next
return None
4. Leetcode 143. 重排链表 https://leetcode.cn/problems/reorder-list/solution/mei-xiang-ming-bai-yi-ge-shi-pin-jiang-t-u66q/
解题思路:
这道题不难,但是有点地方理解很痛苦。
比如说, 为什么要使用l2.next作为循环的判断条件呢?
补充:
下面是找中间节点的代码。
如果链表的节点个数是偶数,fast and fast.next 这个判断条件会找到中间靠右的节点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
return slow
如果判断条件是 fast.next and fast.next.next 会找到中间靠左位置的节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = slow = head
while fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
return slow
对于这道题,我们找中间节点,采用fast and fast.next 这个判断条件,找到的是中间靠右的节点。
如果链表为[1,2,3,4] 我们得到的就是 3->4 翻转后为 4->3
接下来要做的就是
head = 1->2->3
l2 = 4->3
合并两个链表 1->4 4->2 原本head后面就包含了一个3 所以不需要再进行循环了。
我们发现 head 的节点数 是 >= l2的,且head最后的节点与l2最后的节点相同。
所以我们不需要 l2最后的节点,故判断条件为l2.next。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
def reverse_list(head):
if head is None or head.next is None:
return head
new_head = reverse_list(head.next)
head.next.next = head
head.next = None
return new_head
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
"""
Do not return anything, modify head in-place instead.
"""
fast = slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
l2 = reverse_list(slow)
while l2.next:
nxt = head.next
nxt2 = l2.next
head.next = l2
l2.next = nxt
head = nxt
l2 = nxt2
对于这道题,我们找中间节点,采用fast.next and fast.next.next这个判断条件,找到的是中间靠左的节点。
那么判断条件就是head and l2, 因为他们的长度是相等的。
5. Leetcode 234. 回文链表 https://leetcode.cn/problems/palindrome-linked-list/
解题思路:
这题非常简单,找到中间节点,然后翻转中间节点,我们得到head2。
从头遍历head和head2,如果两个链表的值不相等,return False。
循环结束 则说明值都相等,是回文链表,return True。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
def reverse_list(head):
if head is None or head.next is None:
return head
new_head = reverse_list(head.next)
head.next.next = head
head.next = None
return new_head
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
fast = slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
head2 = reverse_list(slow)
while head and head2:
if head.val != head2.val:
return False
head = head.next
head2 = head2.next
return True