快慢指针法总结 链表找环 leetcode 141 142 & 202

前言

  • 这类题,做法很简单,但是我每次证明正确性的时候总是卡壳,所以这次整理一版我个人觉得比较清晰的证明,希望能以后能记住。。

思路

  • 题型特点:给你一个链表,或者只知道递推关系的数据(形如初始是 a0 a 0 ,递推关系是 at=f(at1) a t = f ( a t − 1 ) ),让你在O(1)空间集线性时间里,判断数据是否有环,进一步会问环起点是哪个数据
  • 判断是否有环算法:两个指针,慢指针每次走一步,快指针每次走两步,能相遇则有环,否则无
  • 找环起点算法:在上一步基础上,从相遇点各出发一个指针,每次走一步,再相遇点就是环起点
  • 证明判断有环算法,若有环则一定会相遇:
    • 设在t时刻相遇,无环段长 x1 x 1 , 有环段长 x2 x 2 ,则有
      t=x1+c+k1x22t=x1+c+k2x2 t = x 1 + c + k 1 x 2 2 t = x 1 + c + k 2 x 2
    • 两式相减得到: t=(k2k1)x2 t = ( k 2 − k 1 ) x 2
    • 则能相遇的条件是,满足对任意 x1,x2N x 1 , x 2 ∈ N ,存在
      k1,k2,tNs.t.t=(k2k1)x2,tx1 k 1 , k 2 , t ∈ N s . t . t = ( k 2 − k 1 ) x 2 , t ≥ x 1
    • 显然可以令 k1=0 k 1 = 0 k2 k 2 足够大就可以满足上述条件
  • 证明第二个算法中相遇点是环头:
    • 把上一个证明中的两个等式消元掉t,并换元简化一下式子,得到
      x1=kx2c x 1 = k x 2 − c
    • 我们的目的,是得到 x1 x 1 ,根据等式,显然分别从头和 x1+c x 1 + c 的位置出发,最后会在 x1 x 1 相遇

leetcode相关题目

141. Linked List Cycle

  • 判断链表是否有环
  • 实现:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if (!head)
            return false;
        auto slow = head, fast = head;
        do {
            if (fast->next == NULL || fast->next->next == NULL)
                return false;
            fast = fast->next->next;
            slow = slow->next;
        }while (slow != fast);
        return true;
    }
};

142. Linked List Cycle II

  • 找环头
  • 实现:
/**
 * 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)
            return NULL;
        auto slow = head, fast = head;
        do {
            if (fast->next == NULL || fast->next->next == NULL)
            {
                fast = NULL;
                break;
            }
            fast = fast->next->next;
            slow = slow->next;
        }while (slow != fast);
        if (!fast)
            return NULL;
        slow = head;
        while (slow != fast){
            fast = fast->next;
            slow = slow->next;
        }
        return fast;

    }
};

202. Happy Number

  • 给定next函数和初始值n,next为n的各十进制位的平方和
  • 求最后能否变为1
  • 思路:我们会发现,不管初始n多大,由于是int值,经过一次变换后,最大不会超过1000
  • 完全可以通过判断是否提前有环来解
  • 实现:
class Solution {
public:
    int next(int x){
        int ret = 0;
        while (x > 0){
            ret += (x % 10) * (x % 10);
            x /= 10;
        }
        return ret;
    }
    bool isHappy(int n) {
        int slow = n, fast = n;
        do{
            slow = next(slow);
            fast = next(fast);
            fast = next(fast);
            if (fast == 1)
                return true;
        }while(slow != fast);
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值