[算法学习07]环形链表

前言

快慢指针的使用是本节的核心。

我们有一个链表[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

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值