方法2:hash列表
方法3:快慢指標
環是個什麼東西,查了下,原來就是鍊錶中有元素指向之前有過的元素。那麼如何判斷呢,查了下相關資料,將環形鍊錶的判斷方法做了乙個梳理,並用python和c++實現了一下,記錄如下。
不過由於資料結構不紮實,先用陣列這種順序儲存來作為資料的資料結構吧……………好low!!!
如何判斷鍊錶有環?
為了表示給定鍊錶中的環,我們使用整數 pos 來表示鍊錶尾連線到鍊錶中的位置(索引從 0 開始)。如果 pos 是 -1,則在該鍊錶中沒有環。
示例 1:更通俗的說,比如head=[3,2,0,-4,2],我們發現最後乙個節點是指向第二個節點的(節點編號從1開始)。那麼有幾種判斷方法呢?輸入:head = [3,2,0,-4], pos = 1 輸出:true 解釋:鍊錶中有乙個環,其尾部連線到第二個節點。
示例 2:
輸入:head = [1,2], pos = 0 輸出:true 解釋:鍊錶中有乙個環,其尾部連線到第乙個節點。
示例 3:
輸入:head = [1], pos = -1 輸出:false 解釋:鍊錶中沒有環。
首先從頭節點開始,依次遍歷單鏈表的每乙個節點。每遍歷到乙個新節點,就從頭節點重新遍歷新節點之前的所有節點,用新節點id和此節點之前所有節點id依次作比較。如果發現新節點之前的所有節點當中存在相同節點id,則說明該節點被遍歷過兩次,鍊錶有環;如果之前的所有節點當中不存在相同的節點,就繼續遍歷下乙個新節點,繼續重複剛才的操作。
例如這樣的鍊錶:a->b->c->d->b->c->d, 當遍歷到節點d的時候,我們需要比較的是之前的節點a、b、c,不存在相同節點。這時候要遍歷的下乙個新節點是b,b之前的節點a、b、c、d中恰好也存在b,因此b出現了兩次,判斷出煉表有環。
假設從煉表頭節點到入環點的距離是d,鍊錶的環長是s。那麼演算法的時間複雜度是0+1+2+3+…+(d+s-1) = (d+s-1)*(d+s)/2 , 可以簡單地理解成 o(n*n)。而此演算法沒有建立額外儲存空間,空間複雜度可以簡單地理解成為o(1)。
這種方法最容易想到,當然效率也最低。往往最容易想到或者做到的事情,價值最低,這可以作為乙個普適的真理。
#include #include using namespace std;
int main(),flag=0;
for(int i=1;i網上我看到以id為鍵的雜湊,這種方法會導致hash的數量是線性增長的。為什麼不以鍊錶的值為hash的鍵呢?然後遍歷。如果發現新存入hash的value為0,則繼續遍歷;否則,說明之前已經存過hash的鍵,也就是鍊錶中已經存在當下節點的值,即存在環。
從這個角度考慮環,實際上就是判斷鍊錶中有無相同的元素。這是乙個經典的問題,比較好的方法就是使用hash列表。hash方法的時間複雜度是o(1)*n=o(n),這個時間複雜度已經是最優。最壞情況下空間複雜度是o(n)。空間複雜度與具體問題有關係,比如鍊錶元素是純乙個數字情況下的空間複雜度是o(10),純字母的空間複雜度是o(52)。
鍵值對的方法是數學中函式思想的集中體現
#include #include #include using namespace std;
int main(),flag=0;
mapmap;
for(int i=1;i1:
print("環出口為:{}".format(list[i]))
break;
#環出口為:2
# [finished in 0.1s]
當我寫完這種方法的c++實現時,我意識到了網上使用節點id作為hash的鍵的意義所在,意義就是可以記錄環的入口。而只使用以節點的值為hash列表的鍵時,只能記錄環的出口。所以printf的時候我寫下了「環出口為:」
hash的意義在於減少了一層for迴圈,所以可以大大降低時間複雜度。
首先建立兩個指標1和2,同時指向這個鍊錶的頭節點。然後開始乙個大迴圈,在迴圈體中,讓指標1每次向下移動乙個節點,讓指標2每次向下移動兩個節點,然後比較兩個指標指向的節點是否相同。如果相同,則判斷出煉表有環,如果不同,則繼續下一次迴圈。
例如鍊錶a->b->c->d->b->c->d,兩個指標最初都指向節點a,進入第一輪迴圈,指標1移動到了節點b,指標2移動到了c。第二輪迴圈,指標1移動到了節點c,指標2移動到了節點b。第三輪迴圈,指標1移動到了節點d,指標2移動到了節點d,此時兩指標指向同一節點,判斷出煉表有環。
此方法也可以用乙個更生動的例子來形容:在乙個環形跑道上,兩個運動員在同一地點起跑,乙個運動員速度快,乙個運動員速度慢。當兩人跑了一段時間,速度快的運動員必然會從速度慢的運動員身後再次追上並超過,原因很簡單,因為跑道是環形的。
假設從煉表頭節點到入環點的距離是d,鍊錶的環長是s。那麼迴圈會進行s次(為什麼是s次,有心的同學可以自己揣摩下),可以簡單理解為o(n)。除了兩個指標以外,沒有使用任何額外儲存空間,所以空間複雜度是o(1)。
這是快慢指標的經典應用。
設定兩個指標,乙個每次走一步的慢指標和乙個每次走兩步的快指標。
如果不含有環,跑得快的那個指標最終會遇到 null,說明鍊錶不含環
如果含有環,快指標會超慢指標一圈,和慢指標相遇,說明鍊錶含有環。
public class solution
return false;
}}
判斷環形鍊錶
最開始想到的處理方式是整個陣列,把鍊錶元素乙個乙個放進去,直到有重複的,class solution def hascycle self,head listnode bool tgt while head head head.next if head and head.val in tgt retu...
環形鍊錶判斷
141 環形鍊錶 思路 將訪問過的放到乙個容器中,然後判斷當前訪問的節點是否已經存在容器內 141 1 放到容器中,判斷當前的節點是否在已訪問的容器內 bool hascycle listnode head return false 提交後 5 時間複雜度o n 空間複雜度o n 因為使用了額外的容...
環形鍊錶的判斷
給定乙個鍊錶,判斷鍊錶中是否有環。為了表示給定鍊錶中的環,我們使用整數 pos 來表示鍊錶尾連線到鍊錶中的位置 索引從 0 開始 如果 pos 是 1,則在該鍊錶中沒有環。示例 1 輸入 head 3,2,0,4 pos 1 輸出 true 解釋 鍊錶中有乙個環,其尾部連線到第二個節點。示例 2 輸...