算法套路五——快慢指针

算法套路五——快慢指针

算法示例LeetCode876. 链表的中间结点

给你单链表的头结点 head ,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
在这里插入图片描述

可以设置快慢指针,快指针每次移动两步,慢指针每次移动一步,进过数学推导,可以得出无论数组长度时奇数还是偶数,都可以保证快指针移动到末尾时慢指针移动到数组中间结点,不过要注意的是考虑到由于fast每次走两步,所以循环判断条件为fast and fast.next都不为空

class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow

练习LeetCode141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。如果链表中存在环 ,则返回 true 。 否则,返回 false 在这里插入图片描述

和上题一样,设置快慢指针,当快指针不为空时,快慢指针往前走,然后比较fast与slow,若存在环,则fast一定会追上slow,此时返回true;若没有环,迟早会退出循环,返回false

func hasCycle(head *ListNode) bool {
    
    
    fast,slow:=head,head
    for fast!=nil&&fast.Next!=nil{
    
    
        fast=fast.Next.Next
        slow=slow.Next
        if slow==fast{
    
    
            return true
        }
    }
    return false
}

练习LeetCode142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。在这里插入图片描述

设链表共有 a+b个节点,其中 链表头部到链表入口 有 a 个节点(不计链表入口节点), 链表环 有 b 个节点,设f为fast的走的步数,s为slow走的步数,则有
fast 走的步数是slow步数的 2 倍,即 f = 2s;fast 比 slow多走了 n 个环的长度,即 f = s + nb;
以上两式相减得:f = 2nb,s = nb(即fast和slow 指针分别走了 2n,n 个 环的周长)
从head结点走到入环点需要走 a + nb步, 而slow已经走了nb,那么slow再走a步就是入环点了。
如何知道slow刚好走了a步? 从head开始,和slow指针一起走,相遇时刚好就是a步

func detectCycle(head *ListNode) *ListNode {
    
    
    slow, fast := head, head
    for fast != nil && fast.Next != nil {
    
    
        slow = slow.Next
        fast = fast.Next.Next
        if fast == slow {
    
    
            for slow != head {
    
    
                slow = slow.Next
                head = head.Next
            }
            return slow
        }
    }
    return nil
}

练习LeetCode143. 重排链表

给定一个单链表 L 的头节点 head ,单链表 L 表示为:L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
在这里插入图片描述

这题结合LeetCode876. 链表的中间结点与LeetCode206. 反转链表,先找到中间结点mid,并对后半段进行反转,之后双指针slow与fast不断往后移动进行连接,且注意nxt1,nxt2保留前后两端的链指针


func reorderList(head *ListNode)  {
    
    
    mid:=middleNode(head)
    slow:=head
    fast:=reverseList(mid)
    for fast.Next!=nil{
    
    
        nxt1:=slow.Next
        nxt2:=fast.Next
        slow.Next=fast
        fast.Next=nxt1
        slow=nxt1
        fast=nxt2
    }
}
func middleNode(head *ListNode) *ListNode {
    
    
    slow, fast := head, head
    for fast != nil && fast.Next != nil {
    
    
        slow = slow.Next
        fast = fast.Next.Next
    }
    return slow
}
func reverseList(head *ListNode) *ListNode {
    
    
    var pre *ListNode=nil
    cur:=head
    for cur!=nil{
    
    
        nxt:=cur.Next
        cur.Next=pre
        pre=cur
        cur=nxt
    }
    return pre
}

练习LeetCode234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_45808700/article/details/129717566