判断两个单链表是否有交点并且返回交点的值

一、解决这个问题之前我们需要了解下如何判断一个链表有环?

下面提供二种方法进行实现:

  1. 使用快慢指针,p1为快指针,p2为慢指针,让这两个指针指向链表的头部,让p1每次走一步,p2每次走两步,如果p1和p2相交,则说明这个链表上面有环;否则,p1和p2中任何一个为null,则说明没有环
  2. 使用set集合,将节点往set集合中添加,如果出现不能往set中添加元素的时候,则说明链表是由环的;

二、 判断两个链表是否有交点,输出交点

这个问题分成两种情况,其一是如果链表中不存在环,其二是链表中存在环。

(1)链表中不存在环

基本方法: 先遍历2个链表,求出链表的长度差x;让长链表先走x,后面使得两链表开始跑动,如果遍历的过程中如果出现相等的节点,则链表是有交点的。

代码:

/**
 * 定义单链表
 */
public class Node {
    private int data;
    private Node next;
    public Node(int data){
        this.data = data;
    }
    public void setData(int data){
        this.data = data;
    }
    public int getData(){
        return data;
    }
    public Node getNext(){
        return next;
    }
    public void setNext(Node next){
        this.next = next;
    }
}
 /**
     *判断单链表是否有环,使用快慢指针算法
     */
    public static boolean isHaveLoop(Node node){
        Node p1 = node;
        Node p2 = node;
        while(p1!=null && p2!=null && p2.getNext()!=null){
            p1 = p1.getNext();
            p2 = p2.getNext().getNext();
            if(p1 == p2)
                break;
        }
        return !(p1==null || p2==null); //只要p1或者p2中存在null,则不存在环
    }
/**
     * 不存在环的时候,1.判断是否有交点 2.返回交点
     * (1)计算出两个链表的长度(2)相同位置起始节点是否有交点 isHasPoint并且返回节点
     */
    public int getLen(Node node){
        int len = 0;
        while(node!=null){
            len++;
            node = node.getNext();
        }
        return len;
    }
    public Node getFirstJoinPoint(Node node1,Node node2){
        int len1 = getLen(node1);
        int len2 = getLen(node2);
        int temp = 0;
        if(len1>len2){
            while(true){
            	if(temp == len1-len2)   //先判断下是否相等
                    break;
                node1 = node1.getNext();
                temp++;
            }
        }else{
            while(true){
            	if(temp == len2-len1)
                    break;
                node2 = node2.getNext();
                temp++;  
            }
        }
        while(null != node1 && null != node2){
            if(node1== node2){
                return node1;
            }
            node1 = node1.getNext();
            node2 = node2.getNext();
        }
        return null;
    }

升级版本:如果现在把其中一个链表的首尾相连接,然后只需要判断这个时候另一个单链表是否有环即可。其中需要进行研究问题是如何计算链表进入环的节点。

快指针的速度是慢指针的2倍,所以当假设慢指针走过的路程是:N=L+a

设环的长度为:roundL,快指针走过的路程是:2N=L+k*roundL+a

化简得:L=(k-1)*roundL+(roundL-a)

也就是说链表头到链表进入环的节点之间的距离=快慢指针交点和链表进入环的节点之间的距离+(k-1)(k-1)个环长。

所以,现在只要重新初始化一个新的头指针p3,快慢指针交点为p1(此时p1=p2),让p1和p3同步,每次一位去遍历链表,相交节点即为环的入口节点。

(2)如果上述两个单链表中都是存在环的

肯定有人认为是否存在其中一个有环,另一个没有环这样的情况,这样的情况只是存在于两个链表没有交叉的时候。如果两个链表存在交互且还有环,那么必须是环位于交叉点之后的公共部分,要么是这个环刚好都和这两个链表有交叉点。下面是代码的实现。

这个部分的代码是包含没有环的情况的,思路是先对链表进行判断,如果没有环,使用上述代码(下面做了些改进),反之使用下面的改进的代码。

import org.junit.Test;

public class Main {
    /**
     * 一个链表入环的初始位置
     * 需要进行推导,详情见分析
     */
    public static Node nodeOfIntoLoop(Node node){
    	 Node p1 = node;
         Node p2 = node;
         Node p3 = node;  //保存头结点
         while(p1!=null && p2!=null){
             p1 = p1.getNext();
             p2 = p2.getNext().getNext();
             if(p1 == p2)
                 break;
         }
        while(p1!=p3){
        	p1 = p1.getNext();
        	p3 = p3.getNext();
        }
    	return p3;
    }
    /**
     * 不存在环的时候,1.判断是否有交点 2.返回交点
     * (1)计算出两个链表的长度(2)相同位置起始节点是否有交点 isHasPoint并且返回节点
     */
    public int getLen(Node start,Node end){
        int len = 0;
        while(start!=end){
            len++;
            start = start.getNext();
        }
        return len;
    }
    
    public Node getFirstJoinPoint(Node node1,Node end1,Node node2,Node end2){
        int len1 = getLen(node1,end1);
        int len2 = getLen(node2,end2);
        int temp = 0;
        if(len1>len2){
            while(true){
            	if(temp == len1-len2)   //先判断下是否相等
                    break;
                node1 = node1.getNext();
                temp++;
            }
        }else{
            while(true){
            	if(temp == len2-len1)
                    break;
                node2 = node2.getNext();
                temp++;  
            }
        }
        if(node1== node2){
            return node1;
        }
        while(end1 != node1 && end2 != node2){
            node1 = node1.getNext();
            node2 = node2.getNext();
            if(node1== node2){
                return node1;
            }
        }
         return null;
    }

    /**
     * 实现如果存在环的情况下,如何判断出两个单链表是否有交点,并且求出交点
     */
    public Node getResult(Node node1,Node node2){
    	Node IntoLoop1 = nodeOfIntoLoop(node1);
    	Node IntoLoop2 = nodeOfIntoLoop(node2);
    	if(null == IntoLoop1 && null == IntoLoop2){ //都没有环
    		return getFirstJoinPoint(node1,null,node2,null);
    	}else if((null == IntoLoop1 && null!=IntoLoop2) ||
    			 (null == IntoLoop2 && null!=IntoLoop1)) {
			return null;
		}else { //两个环中都有交点
			if(IntoLoop1 == IntoLoop2){//交点一样,第一种情况
				return getFirstJoinPoint(node1, IntoLoop1, node2, IntoLoop2);
			}else {
				return null; //这种情况下不算是有交点
			}
		}
    }
   
    @Test
    public void test(){
		Node n1 = new Node(1);
		Node n2 = new Node(2);
		Node n3 = new Node(3);
		Node n4 = new Node(4);
		Node n5 = new Node(5);
		Node n6 = new Node(6);
		Node n7 = new Node(7);
		n1.setNext(n4);
		n2.setNext(n3);
		n3.setNext(n4);
		n4.setNext(n5);
		n5.setNext(n6);
		n6.setNext(n7);
		n7.setNext(n6);
		Main main = new Main();
		System.out.println(main.getResult(n1, n2).getData());
    }
}

上述的测试结果是4.

测试用例使用的链表:

 

参考文献:

[1]https://www.jianshu.com/p/634c147fe2a

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值