關於連結串列的面試問題(判斷一個單鏈表中是否有環)
判斷一個單鏈表中是否有環
首先連結串列結點宣告如下:
struct ListNode
{
int key;
ListNode * next;
};
思路:如果一個單鏈表中有環,用一個指標去遍歷,永遠不會結束,所以可以用兩個指標,一個指標一次走一步,另一個指標一次走兩步,如果存在環,則這兩個指標會在環內相遇,時間複雜度為O(n)。
bool HasCircle(ListNode * pHead) { ListNode * pFast = pHead; // 快指標每次前進兩步 ListNode * pSlow = pHead; // 慢指標每次前進一步 while(pFast != NULL && pFast->next != NULL) { pFast = pFast->next->next; pSlow = pSlow->next; if(pSlow == pFast) // 相遇,存在環 return true; } return false; }
用java試下,因為java是沒有指標的,所以需要改動一下。
/* *單鏈表的結點類 */ class LNode{ //為了簡化訪問單鏈表,結點中的資料項的訪問許可權都設為public public int data; public LNode next; } public class LinkListUtli { public static boolean hasCircle(LNode L) { LNode slow=L;//slow表示從頭結點開始每次往後走一步的指標 LNode fast=L;//fast表示從頭結點開始每次往後走兩步的指標 while(fast!=null && fast.next!=null) { if(slow==fast) return true;//p與q相等,單鏈表有環 slow=slow.next; fast=fast.next.next; } return false; } }
拓展問題1:如果單鏈表有環,找出環的入口節點(環的連線點)。
這裡先證明一個定理:碰撞點到連線點的距離=頭指標到連線點的距離
假設單鏈表的總長度為L,頭結點到環入口的距離為a,環入口到快慢指標相遇的結點距離為x,環的長度為r,慢指標總共走了s步,則快指標走了2s步。另外,快指標要追上慢指標的話快指標至少要在環裡面轉了一圈多(假設轉了n圈加x的距離),得到以下關係: s = a + x 2s = a + nr + x =>a + x = nr =>a = nr - x
由上式可知:若在頭結點和相遇結點分別設一指標,同步(單步)前進,則最後一定相遇在環入口結點。
public class LinkListUtli {
//當單鏈表中沒有環時返回null,有環時返回環的入口結點
public static LNode searchEntranceNode(LNode L)
{
LNode slow=L;//p表示從頭結點開始每次往後走一步的指標
LNode fast=L;//q表示從頭結點開始每次往後走兩步的指標
while(fast !=null && fast.next !=null)
{
if(slow==fast) break;//p與q相等,單鏈表有環
slow=slow.next;
fast=fast.next.next;
}
if(fast==null || fast.next==null) return null;
slow=L;
while(slow!=fast)
{
slow=slow.next;
fast=fast.next;
}
return slow;
}
}
拓展問題2:求環的長度。
讓指標指向入口節點,遍歷直到回到入口節點,走過的長度即環的長度。
//求單鏈表環的長度
public static int circleLength(LNode L)
{
LNode p=searchEntranceNode(L);//找到環的入口結點
if(p==null) return 0;//不存在環時,返回0
LNode q=p.next;
int length=1;
while(p!=q)
{
length++;
q=q.next;
}
return length;//返回環的長度
}
思維擴充套件:這裡用到了快慢指標來判斷單鏈表是否有環,快慢指標還能快速解決其他連結串列問題。
一:已知單鏈表的頭指標,查詢到倒數第K個節點
這道題的通俗的解法就是先遍歷一邊連結串列,得到連結串列的長度N,然後再從頭開始遍歷N-K個節點即可
但是現在如果要求只遍歷一遍連結串列的話,該怎麼操作呢?
這時候就可以藉助快指標和慢指標了
我們定義一個快指標P和慢指標Q,先讓P指標走到K個節點位置,然後Q指標從頭指標開始和P一起移動,當P移動到尾部的時候,那麼此時Q節點所在的位置就是倒數第K個節點。
二:已知單鏈表的頭結點,查詢到連結串列的中間節點
這道題的通俗的解法和上面的方法一樣,就是先遍歷一邊連結串列,得到連結串列的長度N,然後再次遍歷N/2個節點即可
但是現在同樣的如果要求之遍歷一邊連結串列的話,該怎麼操作呢?
這時候我們同樣可以藉助快指標和慢指標了
我們定義一個快指標P和慢指標Q,P和Q同時從頭指標出發,快指標P每次移動兩步,慢指標每次移動一步,當快指標P到尾部的時候,慢指標Q所在的位置就是中間節點的位置。