力扣141.環形連結串列
141. 環形連結串列
給定一個連結串列,判斷連結串列中是否有環。
為了表示給定連結串列中的環,我們使用整數 pos 來表示連結串列尾連線到連結串列中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該連結串列中沒有環。示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋:連結串列中有一個環,其尾部連線到第二個節點
示例2:
輸入:head = [1,2], pos = 0
輸出:true
解釋:連結串列中有一個環,其尾部連線到第一個節點。
示例 3:
輸入:head = [1], pos = -1 輸出:false 解釋:連結串列中沒有環。
思路一:雙指標法
1 public class Solution { 2 public boolean hasCycle(ListNode head) { 3 // 快慢指標 4 // 兩指標以不同的速度同時向後移動,如果在某個地方相遇了說明含有環 5 ListNode slow = head, fast = head; 6 while(true){ 7 if(fast == null || fast.next == null){ 8 return false; 9 } 10 slow = slow.next; 11 fast = fast.next.next; 12 if(slow == fast) 13 break; 14 } 15 return true; 16 } 17 }
力扣測試時間為0ms, 空間為39.7MB
複雜度分析:
時間複雜度:我們根據慢指標所走過的路程來作為時間複雜度的估計,假設從連結串列頭結點到入環結點的結點個數為N1, 環的結點的個數為L,前N1的結點的時間是O(N1),下面討論慢指標在後L個結點上所花費的時間。
根據環形跑道相向的追及問題,如果兩個跑步者從同一起點出發,第一次相遇肯定是速度快著比速度慢者多跑了一圈,當兩人相遇時距離他們下次相遇所花費的時間是最大的(假設花費的時間為t),其他情況下距離他們的下次相遇時間都小於這個最大值,所以從現在開始到相遇,速度快者比速度慢者多跑的距離肯定小於一圈。
慢指標入環後,此時快指標已經在環上了,所以距離他們的下次相遇時間肯定小於等於t,所以從現在開始到相遇,快指標比慢指標多跑的距離肯定小於一圈,假設慢指標的速度為v,指標的速度為kv, 那麼kvt - vt <= L, 所以 vt<= L/(k-1), 又因為 k = 2, 所以 vt <= L, vt剛好是慢指標在環上經過的結點個數,所以慢指標入環後最多經過 L 個結點就會被快指標追上,所以慢指標從入環到相遇所花費的時間最大為O(L),所以總的時間為O(t) <= O(N1)+O(L) = O(n)
所以時間複雜度為O(n)
空間複雜度:O(1)
思路二:雜湊法
遍歷連結串列,遍歷過程中嘗試把結點放入集合中,對於每個結點,如果集合中已經存在該結點,說明存在環,返回true
1 public class Solution {
2 public boolean hasCycle(ListNode head) {
3 ArrayList<ListNode> list = new ArrayList<>(); // 儲存結點
4 for(ListNode node = head; node != null; node = node.next){
5 if(list.contains(node)){ // 如果集合中已經存在該結點,說明存在環,返回true
6 return true;
7 }else{
8 list.add(node);
9 }
10 }
11 return false;
12 }
13 }
力扣測試時間為375ms, 空間為39.8MB(這個時間花費著實有點恐怖)
複雜度分析:
時間複雜度:遍歷了一次連結串列,所以時間複雜度為O(n),雖然複雜度是O(n), 但是力扣測試時間卻這麼大,應該是contains()函式的花費比較大,因為contains()不是紅黑樹儲存,所以查詢效率不是很高。
空間複雜度:空間花費主要是集合中元素的個數,最大為O(n)