關於鏈表的面試問題(判斷一個單鏈表中是否有環)


上個月去CVTE面試安卓工程師時,面試官問了一道關於鏈表的算法問題,判斷一個單鏈表中是否有環,當時我沒仔細思考,沒考慮到可能有子環的。

首先鏈表結點聲明如下:

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所在的位置就是中間節點的位置。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM