如何判断两个链表是否相交?如果相交求交点

1.问题描述

        一个比较经典的问题,判断两个链表是否相交,如果相交找出他们的交点。(注意两个单链表相交不会出现X型交叉——单链表,每个节点只有一个指针域)。首先判断两个链表本身是否有环:更多判断单链表是否有环请进传送门。

    /**
     * 判断是否存在环  
     * 步骤:设置两个指针同时指向head,其中一个一次前进一个节点(P1),另外一个一次前进两个节点(P2)。
     * p1和p2同时走,如果其中一个遇到null,则说明没有环,如果走了N步之后,二者指向地址相同,那么说明链表存在环。
     */
    public static boolean isLoop(DataNode h) {
        DataNode p1 = h;
        DataNode p2 = h;
        while(p2.getNext() != null && p2.getNext().getNext()!=null){
            p1 = p1.getNext();
            p2 = p2.getNext().getNext();
            if(p1 == p2) 
                break;
        }
        return !(p1==null||p2==null);
    }

2.两个链表均不含有环

//链表节点
public class DataNode {
    private int data;
    private DataNode next;
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public DataNode getNext() {
        return next;
    }
    public void setNext(DataNode next) {
        this.next = next;
    }
    public DataNode(int data) {
        this.data = data;
    }    
}

image

  • 采用最简单直接的方法,遍历两个链表,判断第一个链表的每个结点是否在第二个链表中,时间复杂度为O(len1*len2),耗时很大;顺序查询到第一个在第二个链表种的节点即是两个链表的交点。
    /**
     * 最环情况下,判断两个链表是否相交,只需要遍历链表,判断尾节点是否相等即可。
     */
    public static boolean isJoinNoLoop(DataNode h1,DataNode h2) {
        DataNode p1 = h1;
        DataNode p2 = h2;
        while(null != p1.getNext())
            p1 = p1.getNext();
        while(null != p2.getNext())
            p2 = p2.getNext();
        return p1 == p2;
    }
  • 先遍历第一个链表到他的尾部,然后将尾部的next指针指向第二个链表头部第一个节点(尾部指针的next本来指向的是null)。这样两个链表就合成了一个链表,判断原来的两个链表是否相交也就转变成了判断新的链表是否有环的问题了:即判断单链表是否有环?这样进行转换后就可以从链表头部进行判断了,其实并不用。通过简单的了解我们就很容易知道,如果新链表是有环的,那么原来第二个链表的头部一定在环上。因此我们就可以从第二个链表的头部进行遍历的,从而减少了时间复杂度(减少的时间复杂度是第一个链表的长度),找到第一个和第一个链表重复的节点即是两个链表的交点;如果找到最后为null,则说明两个单向链表不相交,算法常见上述直接算法。如下所示:

image

  • 如果两个链表相交,又都不存在环,那么不难想象这两个链表共同构成了一个Y型。因此,只要分别遍历这两个链表,找到尾端节点,判断尾端节点是否相同即可确认是否相交。如果要求这种情况的交点,由于相交部分全部都相同,因此,只需要先得到两个链表的差,用两个指针分别指向这两个链表P1,P2假定P1与P2相差为N,那么将P1移动N个节点后,P1与P2同时出发,第一个相等的节点即为交点。

image

    /**
     * 最环情况下找到第一个相交点
     * 方法: 算出两个链表的长度差为x,长链表先移动x步,之后两链表同时移动,直到相遇的第一个交点。
     */
    public static DataNode getFirstJoinNode(DataNode h1,DataNode h2) {
        int length1 = 0;
        int length2 = 0;
        while(null != h1.getNext()) {
            length1 ++;
            h1 = h1.getNext();
        }
        while(null != h2.getNext()) {
            length1 ++;
            h2 = h2.getNext();
        }
        return length1>=length2?getNode(h1,length1,h2,length2):getNode(h2,length2,h1,length1);
    }
  • Hash计数法:如果两个链表相交则两个链表就会有共同的结点;而结点地址又是结点唯一标识。因而判断两个链表中是否存在地址一致的节点,就可以知道是否相交了。可以对第一个链表的节点地址进行hash排序建立hash表,然后针对第二个链表的每个节点的地址查询hash表,如果它在hash表中出现则说明两个链表有共同的结点。这个方法的时间复杂度为:O(max(len1+len2))但同时还得增加O(len1)的存储空间存储哈希表。这样减少了时间复杂度增加了存储空间,以链表节点地址为值,遍历第一个链表,使用Hash保存所有节点地址值,结束条件为到最后一个节点(无环)或Hash中该地址值已经存在(有环)。再遍历第二个链表,判断节点地址值是否已经存在于上面创建的Hash表中。这个方面可以解决题目中的所有情况,时间复杂度为O(m+n),m和n分别是两个链表中节点数量。由于节点地址指针就是一个整型,假设链表都是在堆中动态创建的,可以使用堆的起始地址作为偏移量,以地址减去这个偏移量作为Hash函数;判断出两个链表相交后就是判断他们的交点了。假设第一个链表长度为len1,第二个changdlen2,然后找出长度较长的,让长度较长的链表指针向后移动|len1 - len2| (len1-len2的绝对值),然后在开始遍历两个链表,判断节点是否相同即可。通过上面的操作就可以找到两个链表的交点了。总结上面的几种方法中最后一种是比较不错的,当然hash也是可以的。问题的延伸:如果原来的两个链表中有环怎么处理?

3.链表中有环时

      链表中有环无非两种情况,一种是交于环外,一种交于环内 。如下图所示:

这里写图片描述

这里写图片描述

       通过上图发现如果交于环内,出现了2个相交点,这只是视觉上的2个,其实如果把环看成链表1的,则就可以说链表2相交于链表1的B处;反过来,如果把环看成链表2的,则就可以说链表1相交于链表2的A处。所以我们求相交点时这里就默认输出B,你也可以根据需要自行都输出来。相交于环内和环外求相交点方法是不一样的,那么问题来了如何判断相交于环内还是环外? 思路: 
1.首先获取到2个链表的长度,获得长度差L(因为环是共用的,所以这个差其实就是环外的差),获得2个链表的各个环入口点;
2.先让一个长的链表从头开始走过L个节点;
3.再让短的链表从头与之同步后移,再保证2者不走到环入口的同时判断节点地址信息是否相同;
4.若相同并未到环入口点,则相交于环外,返回相交点,如上图1所示。
5.否则直接返回任意一个链表的环入口作为相交点即可,如上图2所示。 

猜你喜欢

转载自blog.csdn.net/u011635492/article/details/80784894