由力扣141、力扣142引發的力扣287尋找重複數的問題

1. 力扣141判斷是否存在環

雙指針,相當於跑步,一個跑的快A一個跑的慢B,當有環(環周長Q)的時候,肯定快的能再遇到慢的人,此時A比B多跑1-N圈的時候都能互相遇到,所以下面的算法很簡單

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head ==null || head.next ==null){
            return false ;
        }  
    ListNode slow = head;
    ListNode fast = head.next;
    while (slow != fast) {
        if (fast == null || fast.next == null) {
            return false;
        }
        slow = slow.next;
        fast = fast.next.next;
    }
    return true;
 
        
        
    }
}

2. 判斷環的入口

這個很難,先看圖,就會發現規律:
在這裏插入圖片描述

起點爲O,入環口爲N,相遇點爲:M
O到N的距離爲a,N到M的距離爲b,環的長度爲Q
A、B兩個人到達相遇點走過的距離分別是:
A:   a+b
//B和A相遇的時候,B可能已經走了N圈,因爲可能A很長,但是環很小
B:  a+NQ+b
因爲B的速度是A的兩倍,相同的時間走的距離當然也是A的兩倍,所以:
2(a+b)=a+NQ+b
所以:
a+b=NQ
所以
a=NQ-b

2.1 高能預警

a=NQ-b你有沒有發現什麼?

  1. 或者舉例:當N=1的時候,a=Q-b 正是我們要找的環的入口,也就是如果現在能讓A從起點O觸發速度不變,B速度降低爲原來的一半也就是A的速度,這個時候,大家以相同的速度走相同的時間,都能到達環的入口N點,我們就求出來了環的入口
  2. 或者上面還是不是很理解,我們假定N=6,也就是a的距離是6圈減去b的長度,這個時候還讓A回到起點相同速度去找B,B以原來速度的一半在這個環上轉圈,當它轉到第6圈也就是走的長度爲6Q-b的時候,他會發現A,爲什麼?
  3. 因爲6Q-b=a,所以只要把A弄回起點,同時AB的速度保持一致,A去找B ,B在環上繞圈,當A到達環的入口的時候,AB必然會相遇

3. 解題

我們先判斷是否有環,如果有環,那我們定義一個q=head相當於把q放回了起點,同時,我們判斷有環的時候,相交點是p或者p2我們讓q和p都同時繼續走下去,相交的時候就是入口的位置

public class Solution {
    public ListNode detectCycle(ListNode head) {
 
        if (head == null || head.next == null) {
            return null;
        }
        ListNode p = head, p2 = head;
        boolean hasCycle = false;
        while (p2.next != null && p2.next.next != null) {
            p = p.next;
            p2 = p2.next.next;
            if (p == p2) {
                hasCycle = true;
                break;
            }
        }


        if (hasCycle) {
            ListNode q = head;
            while (p != q) {
                p = p.next;
                q = q.next;
            }
            return q;
        } else {
            return null;
        }
 
    }
}

4. 尋找重複數

4.1 題目

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),
可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。

示例 1:

輸入: [1,3,4,2,2]
輸出: 2
示例 2:

輸入: [3,1,3,4,2]
輸出: 3
說明:

不能更改原數組(假設數組是隻讀的)。
只能使用額外的 O(1) 的空間。
時間複雜度小於 O(n2) 。
數組中只有一個重複的數字,但它可能不止重複出現一次。
 

4.2 把數組轉化成鏈表

注意兩點,數組大小N+1,每個數都在1-N之間
說明了什麼,必有一個數是重複的
所以下面這種初始化數組的方式肯定不對

 int arr []=new int[]{1,2,345,54,452,343,23};

比如我們new一個滿足條件的數組並進行初始化

 int arr []=new int[]{2,7,5,3,0,1,2,4,3,8};

得到了下面的數據

類型
下標: 0 1 2 3 4 5 6 7 8 9
值: 2 7 5 3 0 1 2 4 3 8

所以我們用下標組成鏈表的話,肯定是有環的,不信我來試一下

剛開始用下標0來: 251740 251740
我們就發現了環的入口是2

我們的解決算法:

  public int findDuplicate(int[] nums) {
         int i = 0;
         //temp可以初始化爲任何值
        int temp = -100;
        while (true) {
            if (nums[i] == -1) {
            //這次訪問的元素之前那訪問過,返回這次的 下標,
            //如果nums[i]==-1說明這個元素之前訪問過,直接返回即可
                return temp;
            }
            temp = nums[i];
            //這裏的值要和if條件裏面的值保持一致
            nums[i] = -1;
            i = temp;
        }

 
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章