编程之美-判断两个链表是否相交 (涵其扩展问题)

问题定义

两个单向链表的头指针,两个链表都可能带环
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阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容