1、如何判斷一個鏈表是不是有環?
2、如果鏈表為存在環,如果找到環的入口點?
這個算是一個比較老的題目了,之前就看到過,一般通用的做法就是弄兩個指針,一個走得快一點,一個走得慢一點。一般是弄一個走一步,一個走兩步。這樣如果他們相遇,則說明有環。
那么在有環的基礎上,怎么找到這個環的入口呢,一般網上也會給出解釋,可能是我的理解力比較底,網上的解釋中,總是用移動
了s步,又是長度的,總是弄的我很暈,於是,給出我自己的解釋好了,所有的都用移動了多少步來說明。
走一步的指針叫slow,走兩步的叫fast。
相遇的時候,slow共移動了s步,fast共移動了2s步,這個是顯而易見的。
定義a如下: 鏈表頭移動a步到達入口點。
定義x如下: 入口點移動x步到達相遇點。
定義r如下: 從環中的某一點移動r步,又到達的這一點,也就是轉了一圈的意思。
定義t如下: 從相遇點移動到入口點的移動步數
定義L如下: 從鏈表頭移動L步,又到達了相遇點。也就是遍歷完鏈表之后,通過最后一個節點的指針,又移動到了鏈表中的某一點。
其中L = a + r = a + x + t
那么slow和fast相遇了,fast必然比slow多走了n個圈,也就是 n*r 步,那么
s = a + x
2s = s + n*r , 可得 s = n*r
將s=a+x,帶入s =n*r,可得 a+x = n*r, 也就是 a+x = (n-1)*r + r
從表頭移動到入口點,再從入口點移動到入口點,也就是移動了整個鏈表的距離,即是 L = a + r , 所以r = L - a
所以 a+x = (n-1)*r + L - a , 於是 a = (n-1)*r + L - a - x = (n-1)*r + t
也就是說,從表頭到入口點的距離,等於從相遇點繼續遍歷又到表頭的距離。
所以,從表頭設立一個指針,從相遇點設立一個指針,兩個同時移動,必然能夠在入口點相遇,這樣,就求出了相遇點。
代碼如下:
typedef struct node{ int elem; struct node * next; }Node, *NodeList; //尋找環的入口點 NodeList FindLoopPort(NodeList head) { NodeList slow=head,fast=head; //得到相遇點 while(fast && fast->next) { slow=slow->next; fast=fast->next->next; if(slow==fast) break; } if(fast==NULL||fast->next==NULL) return NULL; //slow指向開頭,fast在相遇點 //得到入口點 slow=head; while(slow!=fast){ slow=slow->next; fast=fast->next; } return slow; }
於是我又思考了一下,對於一個帶環的鏈表如何遍歷輸出所有節點的值,因為不能夠通過最后移動到NULL來判斷結束。
如果鏈表設置了尾節點(環入口點)的話,就通過比較是不是又到達了尾節點就可以作為結束條件了
如果沒有設置尾節點的話,那么在遍歷的時候,把每個節點的地址放入一個set集合中,如果下一個地址在set中出現了,那么也就結束了。
不知道有木有其他的方法了。
