編程之美-判斷兩個鏈表是否相交 (涵其擴展問題)

問題定義

兩個單向鏈表的頭指針,兩個鏈表都可能帶環
1: 判斷這兩個鏈表是否相交
2: 如果相交,給出他們相交的第一個節點。

無環情況

判斷鏈表是否相交

單鏈表相交,意味著相交結點具有相同的內存地址,且相交結點后的所有結點是兩個鏈表共有的。
方法一:


Paste_Image.png

如果兩條單鏈表相交,則將鏈表B,連接到鏈表A后面,如圖所示(上面的鏈表是A,下面的鏈表是B),會形成環路,且鏈表B的表頭一定在環上。因此我們只需要從鏈表B開始遍歷,如果可以回到鏈表B的頭結點,則說明兩條鏈表相交。
時間復雜度:O(len(A)+len(B))
代碼如下:

// 結點
static class ListNode{
        int value;
        ListNode next;
    }
static boolean isIntersect1(ListNode h1, ListNode h2){
        boolean isinter = false;
        ListNode p1 = h1, p2 = h2;
        if(p1==null || h2==null) return false;
        // find the end node of list p1
        while(p1.next != null) p1 = p1.next;
        
        // append list p2 on the tail of p1
        p1.next = p2;
        
        // enumerate list p2 from its header
        while(p2 != null){
            if(p2 == h2) {
                isinter = true;
                break;
            }
            p2 = p2.next;
        }

        return isinter;
    }

方法二:
單鏈表相交,意味著相交結點具有相同的內存地址,且相交結點后的所有結點是兩個鏈表共有的。因此如果兩個鏈表相交,則最后一個節點肯定是相同的,因此只需要判斷兩個鏈表的最優一個節點是否相同。
時間復雜度: O(len(A)+len(B))

代碼如下:

static boolean isIntersect2(ListNode h1, ListNode h2){
        ListNode p1 = h1, p2 = h2;
        if(p1==null || h2==null) return false;
        ListNode last1 = p1;
        while(p1.next != null){
            last1 = p1;
            p1 = p1.next;
        }
        ListNode last2 = p2;
        while (p2.next != null){
            last1 = p2;
            p2 = p2.next;
        }
        if(last1==last2){
            return true;
        }else return false;

    }

尋找鏈表的第一個交點

先讓計算鏈表的長度,讓最長的鏈表A先走 len(A)-len(B)步,然后兩個鏈表一起走,第一個相交點,即為所求的點。

static ListNode findFisrtCrossNode(ListNode h1, ListNode h2){
        int lenA = len(h1);
        int lenB = len(h2);
        ListNode p1 = h1, p2 = h2;
        ListNode tmp = null;
        if(lenA > lenB) tmp = p1;
        else tmp = p2;
        for(int i=0; i<Math.abs(lenA-lenB); ++i){
            tmp = tmp.next;
        }
        if(lenA > lenB) p1=tmp;
        else p2 = tmp;
        
        while(p1!=null && p2!=null){
            if(p1==p2) return p1;
            p1 = p1.next;
            p2 = p2.next;
        }
        return null;
    }
static int len(ListNode h){
        int clen = 0;
        ListNode p = h;
        while (p!=null){
            p = p.next;
            ++clen;
        }
        return clen;
    }

有環情況

判斷鏈表是否有環

追逐法:
設置兩個指針 fast, slow,將其初始化為鏈表的頭結點;然后兩個節點同時向前移動,fast一次移動2步,slow一次移動一步。如果存在環,fast指針和slow指針一定相遇。

static boolean hasCycle(ListNode h){
        ListNode fast=h, slow=h;

        while(fast!=null && fast.next!=null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast==slow && slow!=null){
                return true;
            }
        }
        return false;
 }

尋找環在鏈表上的入口點

Paste_Image.png

當fast與slow相遇時,假設slow在環內循環了n次,fast在環內循環了m次,顯然n>m,且m為0 (若有環存在,fast 必然在slow繞環一周之前與slow相遇??紤]極端情況,slow走到環入口節點時,fast位于slow前面的一個節點,記為n0,此時fast以slow指針2倍的速度繞環,當fast指針追上slow指針時, nr/2v = mr/v ==> n/2 = m == n=2m, 即當slow繞環第一周后,回到環入口節點,n=2, 已經繞環兩周,并回到n0節點,由于fast指針步長等于slow的2倍,則fast指針和slow指針必然再環入口節點的前一個節點相遇)
如上圖所示,當slow指針和fast直接相遇時(定義此時的節點為相遇結點),相遇后,另p1指向頭結點,p2指向相遇節點,設頭結點距離環入口節點的距離:a, 頭結點距離相遇節點的距離: s=a+x, 環周長:r=x+y。讓p1、p2指針每次移動一步,當p1==p2時,此時就是p1(p2)指向的節點就是環入口節點。
假設在fast與slow重合時fast已繞環n周(n>0),且此時slow移動總長度為m,則fast移動總長度為2m。
2m = m+ nr = m + n(x+y) ==> m = n(x+y)
m = a+x
==> a+x = n(x+y)
==> a= n(x+y) - x = nr - x
指針p1從鏈表起點處開始遍歷,指針p2從相遇節點處開始遍歷,且p1和p2移動步長均為1。則當p1移動 a 步即到達環的入口點,由上式可知,此時p2也已移動 a 步即nr - x步。由于p2是從相遇節點處開始移動,故p2移動nr步是移回到了相遇節點處,再退 x 步則是到了環的入口點
該公式表明:從鏈表頭和相遇點分別設一個指針,每次各走一步,這兩個指針必定相遇,且相遇的第一個點為環入口點。

代碼如下:

static ListNode findCycleEntry(ListNode h){
        ListNode fast=h, slow=h;
        ListNode meetNode = null;
        while(fast!=null && fast.next!=null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast==slow && slow!=null){
                meetNode = slow;
                break;
            }
        }
        ListNode p = h;
        while(p!=null && meetNode!=null){
            if(p==meetNode){
                return p;
            }
            p = p.next;
            meetNode = meetNode.next;
        }
        return null;
    }

找出帶環的兩條鏈表相交的第一個節點

分兩種情況:
1、只有一條鏈表帶環,此時兩條鏈表不可能相交;否則,由于相交結點后的所有結點由兩條鏈表共享,因此導致另一條不帶環的鏈表卻出現環,導出相悖的結論。
2、兩條鏈表都帶環。如果兩條鏈表相交,則他們共享同一個環!

Paste_Image.png

帶環鏈表相交,如圖所示,存在兩種情況:
1、交點在環中
2、交點不在環中

參考文獻中對該問題的解決辦法是首先找兩個鏈表的相遇點,但由于相遇點值存在于環中(利用fast,slow指針的方式得到的相遇點),因此其方法不能解決交點不在環中的情況。

解決方法
第一步:分別找出兩個鏈表的環入口點pos1, pos2;
第二步:如果pos1==pos2, 屬于第二種情況:交點不在環中。然后以pos1作為兩條鏈表的終點,利用求不帶環單鏈表交點的方法求出交點。
第三步:如果pos1!=pos2, 從pos1開始遍歷環中的節點,如果沒有發現有節點與pos2相等,則說明兩條鏈表沒有交點,否則,存在交點
第四步:分別以pos1和pos2作為終止節點,用求不帶環單鏈表交點的方法求解。其中,必然一個有解,一個無解。取有解的那一組作為我們的答案。

參考文章:

http://blog.csdn.net/linyunzju/article/details/7753548

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容