1. 程式人生 > >快慢指標法總結 連結串列找環 leetcode 141 142 & 202

快慢指標法總結 連結串列找環 leetcode 141 142 & 202

前言

  • 這類題,做法很簡單,但是我每次證明正確性的時候總是卡殼,所以這次整理一版我個人覺得比較清晰的證明,希望能以後能記住。。

思路

  • 題型特點:給你一個連結串列,或者只知道遞推關係的資料(形如初始是 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;
    }
};