兩個(gè)單鏈表相交的一系列問題

【題目】 在本題中,單鏈表可能有環(huán)喷市,也可能無環(huán)相种。給定兩個(gè)
單鏈表的頭節(jié)點(diǎn) head1和head2,這兩個(gè)鏈表可能相交品姓,也可能
不相交寝并。請實(shí)現(xiàn)一個(gè)函數(shù), 如果兩個(gè)鏈表相交腹备,請返回相交的
第一個(gè)節(jié)點(diǎn)衬潦;如果不相交,返回null 即可植酥。 要求:如果鏈表1
的長度為N镀岛,鏈表2的長度為M,時(shí)間復(fù)雜度請達(dá)到 O(N+M)友驮,額外
空間復(fù)雜度請達(dá)到O(1)漂羊。

解題思路

把原問題分成兩個(gè)子問題:1.判斷鏈表是否有環(huán) 2.判斷兩個(gè)鏈表是否相交

子問題1的解法

準(zhǔn)備2個(gè)指針:一個(gè)快指針F(一次走2步)和一個(gè)慢指針S(一次走一步)。如果快指針在走的過程中遇到null卸留,直接返回?zé)o環(huán)走越。但如果有環(huán),快指針和慢指針一定會在環(huán)上相遇耻瑟。

一開始的鏈表結(jié)構(gòu)是這樣的旨指,快指針F和慢指針S都指向頭節(jié)點(diǎn)的位置


在這里插入圖片描述

然后快指針到第3個(gè)節(jié)點(diǎn),慢指針到第2個(gè)節(jié)點(diǎn)


在這里插入圖片描述

下一步喳整,快指針和慢指針的位置如下:
在這里插入圖片描述

下一步:


在這里插入圖片描述

下一步:
在這里插入圖片描述

下一步:
在這里插入圖片描述

這時(shí)谆构,他倆相遇,相遇的時(shí)候框都,將快指針指向頭節(jié)點(diǎn)搬素, 然后快指著由一次走兩步變成一次走一步∥罕#快指針和慢指針一定會在入環(huán)節(jié)點(diǎn)處相遇蔗蹋。(這是結(jié)論,我沒法證明囱淋,但是代碼證明它是對的)
在這里插入圖片描述

代碼實(shí)現(xiàn):
public static Node getLoopNode(Node head) { //判斷單鏈表是否有環(huán)猪杭,并返回第一個(gè)入環(huán)的節(jié)點(diǎn)
        if (head == null || head.next == null || head.next.next == null) {
            //如果不到3個(gè)節(jié)點(diǎn),鏈表一定無環(huán)
            return null;
        }
        Node n1 = head.next; // n1是慢指針
        Node n2 = head.next.next; // n2是快指針
        while (n1 != n2) { //快指針和慢指針相遇時(shí)妥衣,跳出循環(huán)
            if (n2.next == null || n2.next.next == null) {
                return null;
            }
            n2 = n2.next.next;
            n1 = n1.next;
        }
        n2 = head; // 快指針指向頭節(jié)點(diǎn)
        while (n1 != n2) {
            n1 = n1.next;
            n2 = n2.next;
        }
        return n1;
    }

子問題2的解法

兩個(gè)鏈表相交與否有三種情況:
(1)兩鏈表都無環(huán)(不相交)
(2)一個(gè)鏈表有環(huán)皂吮,一個(gè)鏈表無環(huán)(不相交)
(3)兩個(gè)鏈表都有環(huán)戒傻,還分成三種子情況

  • 如下圖所示


    在這里插入圖片描述

    兩種思路
    (1)先遍歷一遍第一個(gè)鏈表,用一個(gè)HashMap,把他的所有節(jié)點(diǎn)放進(jìn)去作為key蜂筹,value不用管需纳。然后遍歷第二個(gè)鏈表,在遍歷過程中查看每個(gè)節(jié)點(diǎn)是否在HashMap里艺挪,這樣就可以找出兩個(gè)鏈表第一個(gè)相交的節(jié)點(diǎn)不翩。
    (2)說起來比較復(fù)雜,直接上代碼麻裳,里面都寫好了注釋口蝠。

  • 判斷兩個(gè)無環(huán)鏈表是否相交:

public static Node noLoop(Node head1, Node head2) { //判斷兩個(gè)無環(huán)鏈表是否相交
        if (head1 == null || head2 == null) {
            return null;
        }
        Node cur1 = head1;
        Node cur2 = head2;
        int n = 0;
        while (cur1.next != null) { //計(jì)算出鏈表1的長度
            n++;
            cur1 = cur1.next;
        }
        while (cur2.next != null) { //計(jì)算出鏈表1和2的長度差值n
            n--;
            cur2 = cur2.next;
        }
        if (cur1 != cur2) {
            return null;
        }
        //把長鏈表的頭節(jié)點(diǎn)賦給cur1,短鏈表的頭節(jié)點(diǎn)賦給cur2
        cur1 = n > 0 ? head1 : head2;
        cur2 = cur1 == head1 ? head2 : head1;
        n = Math.abs(n);
        while (n != 0) { //長鏈表的cur1先走n步津坑,然后與短鏈表的cur2一起走(相交)
            n--;
            cur1 = cur1.next;
        }
        while (cur1 != cur2) { //cur1和cur2相遇的時(shí)候退出循環(huán)
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1; // 返回第一個(gè)入環(huán)的節(jié)點(diǎn)
    }
  • 判斷兩個(gè)有環(huán)鏈表是否相交:
public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) { //判斷兩個(gè)有環(huán)鏈表是否相交
        Node cur1;
        Node cur2;
        if (loop1 == loop2) { //復(fù)用noLoop方法
            cur1 = head1;
            cur2 = head2;
            int n = 0;
            while (cur1 != loop1) {
                n++;
                cur1 = cur1.next;
            }
            while (cur2 != loop2) {
                n--;
                cur2 = cur2.next;
            }
            cur1 = n > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            n = Math.abs(n);
            while (n != 0) {
                n--;
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } else {
            cur1 = loop1.next;
            while (cur1 != loop1) {
                if (cur1 == loop2) {
                    return loop1;
                }
                cur1 = cur1.next;
            }
            return null;
        }
    }
既然必要的子問題解決方法都寫完了妙蔗,我們來解決原問題,代碼如下:
public static Node getIntersectNode(Node head1, Node head2) { //判斷兩個(gè)鏈表是否相交
        if (head1 == null || head2 == null) {
            return null;
        }
        Node loop1 = getLoopNode(head1);
        Node loop2 = getLoopNode(head2);
        if (loop1 == null && loop2 == null) {
            return noLoop(head1, head2);
        }
        if (loop1 != null && loop2 != null) {
            return bothLoop(head1, loop1, head2, loop2);
        }
        return null;
    }

==所有程序代碼:==

public class FindFirstIntersectNode {

    public static class Node {
        public int value;
        public Node next;

        public Node(int data) {
            this.value = data;
        }
    }

    public static Node getIntersectNode(Node head1, Node head2) { //判斷兩個(gè)鏈表是否相交
        if (head1 == null || head2 == null) {
            return null;
        }
        Node loop1 = getLoopNode(head1);
        Node loop2 = getLoopNode(head2);
        if (loop1 == null && loop2 == null) {
            return noLoop(head1, head2);
        }
        if (loop1 != null && loop2 != null) {
            return bothLoop(head1, loop1, head2, loop2);
        }
        return null;
    }

    public static Node getLoopNode(Node head) { //判斷單鏈表是否有環(huán)疆瑰,并返回第一個(gè)入環(huán)的節(jié)點(diǎn)
        if (head == null || head.next == null || head.next.next == null) {
            //如果不到3個(gè)節(jié)點(diǎn)眉反,鏈表一定無環(huán)
            return null;
        }
        Node n1 = head.next; // n1是慢指針
        Node n2 = head.next.next; // n2是快指針
        while (n1 != n2) { //快指針和慢指針相遇時(shí),跳出循環(huán)
            if (n2.next == null || n2.next.next == null) {
                return null;
            }
            n2 = n2.next.next;
            n1 = n1.next;
        }
        n2 = head; // 快指針指向頭節(jié)點(diǎn)
        while (n1 != n2) {
            n1 = n1.next;
            n2 = n2.next;
        }
        return n1;
    }

    public static Node noLoop(Node head1, Node head2) { //判斷兩個(gè)無環(huán)鏈表是否相交
        if (head1 == null || head2 == null) {
            return null;
        }
        Node cur1 = head1;
        Node cur2 = head2;
        int n = 0;
        while (cur1.next != null) { //計(jì)算出鏈表1的長度
            n++;
            cur1 = cur1.next;
        }
        while (cur2.next != null) { //計(jì)算出鏈表1和2的長度差值n
            n--;
            cur2 = cur2.next;
        }
        if (cur1 != cur2) {
            return null;
        }
        //把長鏈表的頭節(jié)點(diǎn)賦給cur1穆役,短鏈表的頭節(jié)點(diǎn)賦給cur2
        cur1 = n > 0 ? head1 : head2;
        cur2 = cur1 == head1 ? head2 : head1;
        n = Math.abs(n);
        while (n != 0) { //長鏈表的cur1先走n步寸五,然后與短鏈表的cur2一起走(相交)
            n--;
            cur1 = cur1.next;
        }
        while (cur1 != cur2) { //cur1和cur2相遇的時(shí)候退出循環(huán)
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1; // 返回第一個(gè)入環(huán)的節(jié)點(diǎn)
    }

    public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) { //判斷兩個(gè)有環(huán)鏈表是否相交
        Node cur1;
        Node cur2;
        if (loop1 == loop2) { //復(fù)用noLoop方法
            cur1 = head1;
            cur2 = head2;
            int n = 0;
            while (cur1 != loop1) {
                n++;
                cur1 = cur1.next;
            }
            while (cur2 != loop2) {
                n--;
                cur2 = cur2.next;
            }
            cur1 = n > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            n = Math.abs(n);
            while (n != 0) {
                n--;
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } else {
            cur1 = loop1.next;
            while (cur1 != loop1) {
                if (cur1 == loop2) {
                    return loop1;
                }
                cur1 = cur1.next;
            }
            return null;
        }
    }

    public static void main(String[] args) {
        // 1->2->3->4->5->6->7->null
        Node head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);

        // 0->9->8->6->7->null
        Node head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).value);

        // 1->2->3->4->5->6->7->4...
        head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);
        head1.next.next.next.next.next.next = head1.next.next.next; // 7->4

        // 0->9->8->2...
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next; // 8->2
        System.out.println(getIntersectNode(head1, head2).value);

        // 0->9->8->6->4->5->6..
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).value);

    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市耿币,隨后出現(xiàn)的幾起案子梳杏,更是在濱河造成了極大的恐慌,老刑警劉巖掰读,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叭莫,居然都是意外死亡蹈集,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門雇初,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拢肆,“玉大人,你說我怎么就攤上這事靖诗」郑” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵刊橘,是天一觀的道長鄙才。 經(jīng)常有香客問我,道長促绵,這世上最難降的妖魔是什么攒庵? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任嘴纺,我火速辦了婚禮,結(jié)果婚禮上浓冒,老公的妹妹穿的比我還像新娘栽渴。我一直安慰自己,他們只是感情好稳懒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布闲擦。 她就那樣靜靜地躺著,像睡著了一般场梆。 火紅的嫁衣襯著肌膚如雪墅冷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天辙谜,我揣著相機(jī)與錄音俺榆,去河邊找鬼。 笑死装哆,一個(gè)胖子當(dāng)著我的面吹牛罐脊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜕琴,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼萍桌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凌简?” 一聲冷哼從身側(cè)響起上炎,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雏搂,沒想到半個(gè)月后藕施,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凸郑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年裳食,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芙沥。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诲祸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出而昨,到底是詐尸還是另有隱情救氯,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布歌憨,位于F島的核電站着憨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏务嫡。R本人自食惡果不足惜享扔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一底桂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惧眠,春花似錦籽懦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽或链。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工挑庶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留查排,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓露戒,卻偏偏與公主長得像荠锭,于是被迫代替她去往敵國和親甫贯。 傳聞我的和親對象是個(gè)殘疾皇子供炎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容