單鏈表是否有環(Java版),單鏈java
題目:判斷一個帶頭結點的單鏈表L是否有環
解題思路:
咋看之下,這題還真不好解,於是又一些投機取巧的人,在訪問單鏈表中元素的時候,用一個比較大的數N控制訪問元素的個數,他們認為如果在訪問到第N個元素之前有一個元素為null,那麼單鏈表沒有環,反之,單鏈表有環。他們的解釋是都訪問了這麼多元素了,還沒有一個元素為空白,單鏈表很有可能有環,再加上一般單鏈表中的元素都不超過N個,所以單鏈表有環。對於這種解答方式,我們也只能呵呵了。
要判斷單鏈表有沒有環,肯定會判斷兩個指標是否相等。而且這兩個指標從頭結點位置開始走的步數肯定不一樣。可以設兩個指標slow,fast,slow從頭結點開始往後走,每次走一步,同時fast從頭結點開始往後走,每次走兩步,這兩個指標一直往後面走直到fast為空白,則單鏈表無環,或者slow與fast相等,則單鏈表有環。這個過程可以保證slow會走過所有在它之前的結點,同時fast比slow多走的步長從1開始每次遞增1,而且當它們相遇時多走的步長就是環中結點的個數的正整數倍。
ADT定義:
//單鏈表的結點類class LNode{//為了簡化訪問單鏈表,結點中的資料項目的存取權限都設為publicpublic int data;public LNode next;}
演算法實現:
public class LinkListUtli {public static boolean hasCircle(LNode L){if(L==null) return false;//單鏈表為空白時,單鏈表沒有環if(L.next==null) return false;//單鏈表中只有頭結點,而且頭結點的next為空白,單鏈表沒有環LNode p=L.next;//p表示從頭結點開始每次往後走一步的指標LNode q=L.next.next;//q表示從頭結點開始每次往後走兩步的指標while(q!=null) //q不為空白執行while迴圈{if(p==q) return true;//p與q相等,單鏈表有環p=p.next;q=q.next.next;}return false;}}
拓展:判斷帶頭結點的單鏈表是否有環,並找出環的入口結點
解題思路:
不妨設從頭結點到環的入口結點需要走a步即環外包括頭結點在內總共有a個結點,環中有b個結點,假設slow走了s步後,slow與fast第一次相遇。它們肯定是在環內相遇,而且相遇時slow沒有從環的入口結點再次走到環的入口結點,假設在環中距離環入口結點d步長距離相遇,設相遇結點為meet。
此時令頭結點指向fast,讓slow與fast每次往後走一步,當它們再次第一次相遇時,相遇的結點就是環的入口結點。
證明如下:
當slow再走s步後會再次到meet結點,而此時fast走了s步後也會首次到達meet結點,它們相遇,因為兩者都是每次同時走一步,那麼從fast進入環中開始,fast與slow就一直相遇,它們首次相遇的結點就是環的入口結點。
public class LinkListUtli {//當單鏈表中沒有環時返回null,有環時返迴環的入口結點public static LNode searchEntranceNode(LNode L){if(L==null) return null;//單鏈表為空白時,單鏈表沒有環if(L.next==null) return null;//單鏈表中只有頭結點,而且頭結點的next為空白,單鏈表沒有環LNode p=L.next;//p表示從頭結點開始每次往後走一步的指標LNode q=L.next.next;//q表示從頭結點開始每次往後走兩步的指標while(q!=null) //q不為空白執行while迴圈{if(p==q) break;//p與q相等,單鏈表有環p=p.next;q=q.next.next;}if(q==null) return null;//這裡之所以沒有向上面一樣,先讓p,q走一步再進入迴圈判斷,是因為頭結點可能就是環的入口結點q=L;while(q!=null){if(p==q) return p;//返迴環中入口結點p=p.next;q=q.next;}return null;}}
拓展:判斷帶頭結點的單鏈表是否有環,並求環的長度
解題思路:
設一個指標q指向環入口結點,讓q往後移動直到q再次等於環的入口結點,此時q所走的總步數就是環的長度
public class LinkListUtli {//當單鏈表中沒有環時返回null,有環時返迴環的入口結點public static LNode searchEntranceNode(LNode L){if(L==null) return null;//單鏈表為空白時,單鏈表沒有環if(L.next==null) return null;//單鏈表中只有頭結點,而且頭結點的next為空白,單鏈表沒有環LNode p=L.next;//p表示從頭結點開始每次往後走一步的指標LNode q=L.next.next;//q表示從頭結點開始每次往後走兩步的指標while(q!=null) //q不為空白執行while迴圈{if(p==q) break;//p與q相等,單鏈表有環p=p.next;q=q.next.next;}if(q==null) return null;//這裡之所以沒有向上面一樣,先讓p,q走一步再進入迴圈判斷,是因為頭結點可能就是環的入口結點q=L;while(q!=null){if(p==q) return p;//返迴環中入口結點p=p.next;q=q.next;}return null;}//求單鏈表環的長度public static int circleLength(LNode L){LNode p=searchEntranceNode(L);//找到環的入口結點if(p==null) return 0;//不存在環時,返回0LNode q=p.next;int length=1;while(p!=q){length++;q=q.next;}return length;//返迴環的長度}}