1、判断两个链表是否相交,并返回交点
1.1 简单描述
其实这是一个综合题,属于链表里面最难的了(好像面试管特别喜欢考这个),因为判断链表相交之前你需要先判断链表是否有环
1.2 思路
- 先判断两个链表是否有环(两种方法,辅助hashset或用快慢指针),找到入环点
- 两个都无环,判断相交(两种方法,辅助hashset或计算链表长度差N然后长链表先走N步,然后一起走,相遇即为交点,也可以将两个链表放到两个栈里,一起弹栈)
- 两个都有环(一个有环一个没环肯定是不相交的,因为如果相交就是两个有环链表了,相悖),看两个环的入环点是否一样,分三种情况,具体请看下面的手绘图
具体代码实现如下:
package cn.nupt;
import java.util.HashSet;
/**
* @Description: 如果两链表相交,输出第一个相交的节点(前面还会考虑这两个链表是否有环)
*
* @author PizAn
* @Email pizan@foxmail.com
* @date 2019年2月19日 下午11:11:20
*
*/
public class FindFirstIntersectNode {
// 构建链表结构,这里是静态内部类
public static class Node {
int value;
Node next;
// 再来个构造方法
public Node(int value) {
this.value = value;
}
}
// 判断两个链表是否相交,并返回相交点
public static Node findFirstIntersectNode(Node head1, Node head2) {
// 先来个鲁棒性判断
if (head1 == null || head2 == null) {
return null;
}
// 1、看两个链表是否有环,并返回入环点loopPoint
Node loopPoint1 = ishuanWithTwoPoint(head1);
Node loopPoint2 = ishuanWithTwoPoint(head2);
// 2、如果都没环
if (loopPoint1 == null && loopPoint2 == null) {
// 都不是环
return noLoop(head1, head2);
} else if (loopPoint1 != null && loopPoint2 != null) {
// 都是环
return bothLoop(head1, head2, loopPoint1, loopPoint2);
}
// 一个有环一个无环,肯定不相交(相交了就变成两个有环了,相悖),直接返回null
return null;
}
// 1、判断链表是否有环(用外部辅助结构:hashSet或者hashMap,将节点当作键存入集合(set/map不能存重复的键),然后调用集合的contains(Object
// key)的方法
// 如果检测到重复存储就说明有环,如果没有,就是无环)
public static Node ishuanWithHashSet(Node head) {
// 这里返回入环点
HashSet<Node> set = new HashSet<Node>();
// HashMap<Node, Node> map = new HashMap<Node, Node>();
// 感觉这种用map或者set的方法好像不行,因为单链表你把节点存到集合中的时候,也有可能
// (我擦,突然想起来吗,这个存的是Node,就算Node.value一样,这个Node.next也不一样的,所以,可行!!!)
Node cur = head;
while (cur != null) {
if (set.contains(cur)) {
// 检测是否重复存储
return cur; // 如果重复存储了,这个点就是入环点
}
set.add(cur);
cur = cur.next;
}
return null;
}
// 第二种方法,用快慢指针,快走2,慢走1,若相遇则有环,再来一个指针从head开始,和这个相遇点指针一起走,再相遇就是入环点(可以自己画一画,挺玄学的)
public static Node ishuanWithTwoPoint(Node head) {
if (head == null) {
return head;
}
Node slow = head;
Node fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
// 相遇,说明有环
Node cur = head;
while (cur != slow) {
// 来一个指针从head开始,和这个相遇点指针一起走,再相遇就是入环点
cur = cur.next;
slow = slow.next;
}
return slow;
}
}
return null; // 什么都没有,就是无环
}
// 说完了有环没环,下面开始说相交
// 1、两个都无环,第一种是将表1放到set中,再遍历表2,若重复contains,则相交,且为交点(挺简单的,在此不表)
// 2、两个都无环,第二种是遍历两个链表,如果到最后都没相交,就没有,如果相交,分别计算两个链表的长度,相差为n,长的先跑n步,然后一起跑,第一次相遇的就是交点
// 1、两个都有环,有三种情况,具体请看手绘图
private static Node noLoop(Node head1, Node head2) {
// 1、先知道两个链表的长度
int long1 = 0;
int long2 = 0;
Node cur1 = head1;
Node cur2 = head2;
while (cur1.next != null) {
long1++;
cur1 = cur1.next;
}
while (cur2.next != null) <