爲什麼鏈表會誤成環
來看一個栗子,最近刷題刷鏈表,經常一不小心就下弄成環然後死循環了。
//ListNode* reverseList(ListNode* head)
//{
// ListNode* node_temp;
// ListNode* new_head;
//
// node_temp = head;
// //遍歷一個節點,就把它拿下來放到頭去
// while (head->next != NULL)
// {
// //先考慮只又兩個節點的情況
// head = head->next;
// new_head = head;
// new_head->next = node_temp;
// node_temp = new_head;
// }
// return new_head;
//}
像這樣的,用原有節點進行賦值,就容易成環。
因爲當我把一個原有節點直接掛載到另外一個節點之下時,其實是將其後續節點一併帶過去了。
正確的做法應該是新建一個節點,然後將需要掛載的節點的內容拷貝到新節點裏面,使用新節點去掛載。
如果對鏈表操作不熟,建議先了解一下:回顧通用鏈表 - 親測代碼示例
當然,我的好兄弟以爲他很瞭解鏈表,反正他這兩天一直繞在環裏面出不來。
如何判斷鏈表有環
喔,把你的環,我的環,串一串,串成一個同心圓,出不去,死循環。
其實說簡單也簡單,快慢指針就解決了,快指針兩步走,慢指針一步走,只要兩個指針重合了,那就說明有環,因爲快指針繞回來了。
時間複雜度爲線性,空間複雜度爲常數。
說不簡單也不簡單,因爲你去判斷一個鏈表是否有環,那頂多是在測試環節,放在發佈環節未免顯得太刻意,連代碼是否安全都不能保證。
而且,有環的話一般是運行不過的,不用測,運行超時妥妥要考慮一下環的情況,一調試就知道了。
如何判斷環的大小
有了上面的基礎,其實判斷大小是很簡單的。當快慢指針相遇的時候,別停,繼續走,並開始計數,當再次相遇的時候,慢指針走了多少位置,那就是環的長度。
判斷環的入口
這個就比較有意思了,這個不畫圖可能聽不明白。
我這個人,懶了點,來張現成的圖吧。
看啊,在相遇之前呢,慢指針走的距離很好求的:L1 = D+S1;
快指針走的距離:設它在相遇前繞了n圈(n>1),那麼:L2 = D+S1+n(S1+S2);
不過,還有一個等量關係,不要忽略掉,快指針的速度是慢指針的兩倍,所以:L2 = 2L1;
那麼就是:n(S1+S2)-S1 = D;
再轉換一下就是:(n-1)(S1+S2)+S2 = D;
那也就是說,在相遇時候,把一個慢指針放在鏈表頭,開始遍歷,把一個慢指針放在相遇點開始轉圈,當它倆相遇的時候,就是入環點了。
其實吧,用腦子想一開始很難想出來,用手想就快多了。