阿龙的学习笔记---快慢指针算法:寻找链表中是否有环路等题目

参考:https://leetcode-cn.com/problems/find-the-duplicate-number/solution/qian-duan-ling-hun-hua-shi-tu-jie-kuai-man-zhi-z-3/

  • 快慢指针
    • 字面意思就是步长一个快一个慢的两个指针, 最开始引申出快慢指针应该是用来解决链表中的环路问题。

      假如一个有环路的链表,以步长为2和步长为1的两个指针,一直走下去一定会有相遇的时刻。
      在这里插入图片描述
      假如上面这个有环路的链表,试试就发现,两个指针会有相遇的时候。这时候fast走了2X步,则slow走了X步,且他们相遇了。

      这说明fast多走了X步,X是整数倍的环路,假如环路周长为C,说明 X%C=0

      我们要找到的是环路的入口,现在快慢指针相遇的地方不一定是入口。我们假设入口位置是 Y 步的位置。

      现在slow指针以及走了 X 步,X 步包含从 0-Y 的部分是在环外,而X-Y的部分是在环内。由于X%C=0,所以slow指针再走Y步则能到达入口位置。而我们这时选一个新指针从头开始走,也是走Y步即可到达入口位置,所以这两个指针相遇的位置即为环路入口位置。


  • 例题1:寻找环形列表的环的入口节点(Leetcode#142)

    这题目描述有点乱七八糟,反正就是一个链表,给你头结点head,若有环路则找出入口处节点指针返回,如果没有则返回NULL。
    在这里插入图片描述
    使用快慢指针来解决这个题目:

    /**最常见的链表结构
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        ListNode *detectCycle(ListNode *head) {
            if(head == nullptr)
                return nullptr;
            ListNode* fast = head, slow = head;	//先定义slow、fast两个指针指向head
            do{							//快慢指针一直往后跑,直到两个相等;
                if(fast->next)			//如果快指针跑到没地方跑了,则无环路,返回NULL退出。
                {
                    fast = fast->next;
                    if(fast->next)
                        fast = fast->next;        
                    else
                        return NULL;
                }else
                    return NULL;
                if(slow->next)
                    slow = slow->next;
            }
            while(fast != slow);
            
            ListNode* finder = head;
            //这里finder指针从头开始,直到和slow相遇。
            //注意:上面是do...while,这里可不能直接复制,这里有可能一开始就是入口!
            while(finder != slow){		
                slow = slow->next;
                finder = finder->next;
            }
            //相遇时finder则就是入口位置
            return finder;
        }
    };
    

  • 例题2:寻找重复数(Leetcode#287)
    在这里插入图片描述
    这道题一上来看起来与链表环路没关系啊?可是仔细分析:数组长度1+n,数字在1-n之间,假如我们把位置 i 的数字 nums[i] 当做链表下一个位置,那么如果有重复数字则一定会形成环路。环路的入口就是这个数字。
    class Solution {
    public:
        int findDuplicate(vector<int>& nums) {
            int slow = 0;
            int fast = 0;
            do
            {
                slow = nums[slow];
                fast = nums[fast];
                fast = nums[fast];
            }
            while(slow != fast);
            int finder = 0;
            while(slow != finder)
            {
                slow = nums[slow];
                finder = nums[finder];
            }
            return finder;
        }
    };
    
    假如{1,3,3,2,4}这个例子,重复的“3”就是环路的入口,在存储“3”的两个1,2位置其实意义表示的是同一个节点,指向了3号位置。所以快慢指针相遇的时候,slow指向了第一个3,fast指向了第二个3,这不是bug。

  • 例题3、4、5:用到了快慢指针,但没有用到环路性质
    • 例题3:删除链表倒数第n个节点 (Leetcode #19)

      这好像是剑指上的例题,我们slow和fast间隔n,slow在0,fast在n;当fast到null的时候,slow就是倒数第n个了。一次遍历找到节点,删除即可。

    • 例题4:链表的中间节点 (Leetcode #876)

      同样适用于快慢指针,fast步长2,slow步长1,fast到末尾则slow在中间。

      注意偶数长度和奇数长度。

    • 例题5:判断回文链表(Leetcode#234)

      回文链表大概意思就是从中间对称嘛,那根据上一题可以找到链表中间位置,那么从中间开始向两边走并比较即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值