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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luke2834/article/details/79393145

前言

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

思路

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

猜你喜欢

转载自blog.csdn.net/luke2834/article/details/79393145