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
你有沒有發現什麼?
- 或者舉例:當N=1的時候,
a=Q-b
正是我們要找的環的入口,也就是如果現在能讓A從起點O觸發速度不變,B速度降低爲原來的一半也就是A的速度,這個時候,大家以相同的速度走相同的時間,都能到達環的入口N點,我們就求出來了環的入口 - 或者上面還是不是很理解,我們假定N=6,也就是a的距離是6圈減去b的長度,這個時候還讓A回到起點相同速度去找B,B以原來速度的一半在這個環上轉圈,當它轉到第6圈也就是走的長度爲6Q-b的時候,他會發現A,爲什麼?
- 因爲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;
}
}