//Java版.左老师给出的代码,很赞
//package problems_2017_08_16;
public class Problem_03_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) {
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) {
if (head == null || head.next == null || head.next.next == null) {
return null;
}
Node n1 = head.next; // n1 -> slow
Node n2 = head.next.next; // n2 -> fast
while (n1 != n2) {
if (n2.next == null || n2.next.next == null) {
return null;
}
n2 = n2.next.next;
n1 = n1.next;
}
n2 = head; // n2 -> walk again from head
while (n1 != n2) {
n1 = n1.next;
n2 = n2.next;
}
return n1;
}
public static Node noLoop(Node head1, Node head2) {
if (head1 == null || head2 == null) {
return null;
}
Node cur1 = head1;
Node cur2 = head2;
int n = 0;
while (cur1.next != null) {
n++;
cur1 = cur1.next;
}
while (cur2.next != null) {
n--;
cur2 = cur2.next;
}
if (cur1 != cur2) {
return null;
}
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;
}
public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
Node cur1 = null;
Node cur2 = null;
if (loop1 == loop2) {
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);//output:6
// 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.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);//output:2
// 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);//output:4
System.out.println(getIntersectNode(head2, head1).value);//note the order //output:6
}
}
然后照着程序执行流程梳理思路:
0.判断两链表是否有环(分别找环入口结点,能找到则有环,否则无环):
若都无环,转入第1步(可能是情况1或2);
若都有环,转入第2步(可能是情况4或5或6);
若一个有环一个无环,直接返回NULL,因为如果他们相交,是不可能一个有环一个无环的(图中情况3);
1.都无环的情况,退化到两个无环链表找入口点的问题(可参见<剑指offer>和leetcode:Intersection of Two Linked Lists)
1.0 先判断两条链表的长度;
1.1 从头节点开始走,更长的链表先走"长度之差"步,然后一起走,如果相遇,则为入口点(情况2);否则无交点(情况1)
2.都有环的情况,这种情况还要细分:
2.0 先判断两链表环入口点是否相同,若相同,则为情况4,转入步骤2.1;若不同,则为情况5或6,转入2.2;
2.1 如果为上图中情况4,我们可以把两链表交点作为"假想的尾部节点",然后就退化成两个无环链表找交点的问题了;
2.2 为判断两链表是否有交点,我们可以从第一个环的入口节点的下一个节点开始next,如果遇到了第二个链表环的入口节点,则返回第一个链表的入口节点(情况5:题目说找出第一个相交的节点,其实我觉得返回第二个链表的入口节点也行);反之,若走了一圈没遇到第二个链表环的入口节点,说明两链表不相交(情况6);
至此,程序执行结束.设计很巧妙,要熟练掌握.
另外一段代码:
//综合:链表噩梦题之一:2条链表相交问题,链表可能有环,也可能无环,求交点
public static Node nightMireCrossNode(Node head1, Node head2){
if (head1 == null || head2 == null) return null;
Node loop1 = getLoopNodeFace(head1);
Node loop2 = getLoopNodeFace(head2);
//(1)`loop1 == null && loop2 == null` 俩都无环,求相交点
if (loop1 == null && loop2 == null) return noLoopNodeGetCross(head1, head2);
//(2)`if(loop1 != null && loop2 == null)`
// 或者:`if(loop1 == null && loop2 != null)`
// 其中一个有环,有一个无环,求相交点
else if (loop1 != null && loop2 != null) return bothHasLoopCross(head1, head2, loop1, loop2);
//(3)`if(loop1 != null && loop2 != null)` 俩都有环,求相交点
else return null;//压根没有的
}
//对于环形链表来说,**求入环节点**就是一个玄学的数学推理
//咱不用推,直接记住以下结论:
//
//(1)初始化快慢指针:slow先走1步,fast先走2步;
//(2)如果快指针,先遇到null,则根本就不可能有环,返回null
//(3)有环的话,slow和fast一定会相遇,这第一次相遇就证明了有环,就是这么巧!!!
//(4)怎么找到那个入环节点呢?让fast回到head,和slow同步慢走(都只走1步),第二次相遇时,这个点,就是入环节点。
public static Node getLoopNodeFace(Node head){
if (head == null || head.next == null) return null;
//(1)初始化快慢指针:slow先走1步,fast先走2步;
Node slow = head.next;
Node fast = head.next.next;
//(2)如果快指针,先遇到null,则根本就不可能有环,返回null
while (slow != fast){
if (fast.next == null || fast.next.next == null) return null;
//(3)有环的话,slow和fast一定会相遇,这第一次相遇就证明了有环,就是这么巧!!!
fast = fast.next.next;
slow = slow.next;
}
//(4)怎么找到那个入环节点呢?
// 让fast回到head,和slow同步慢走(都只走1步),第二次相遇时,这个点,就是入环节
fast = head;
while (slow != fast){
slow = slow.next;
fast = fast.next;//同步走
}
//第二次相遇,必然是入环节点
return slow;
}
//给你2个链表和他们的入环节点,如果都没有环,求相交点
//(1)让cur1走head1,cur2走head2
//(2)如果走完之后,发现,cur1 = cur2,显然就是相交的
//如何确定相交的交点呢?
//(3)如果长的那根链表长度为n1,短的那根链表长度为n2,因为末端都是相同的,则必然
//n1-n2就是交点之前,head1比head2多出来的长度
//故,让cur从新从head1走n1-n2,再让head2和head1同步走,必定同时走到交点处。
public static Node noLoopNodeGetCross(Node head1, Node head2){
//既然没有环,那也不会有入环节点,就不用串loo1和loop2了
if (head1 == null || head2 == null) return null;//压根没有第二条链表
//用n来模拟长链和断链长度的差
int n = 0;
//(1)让cur1走head1,cur2走head2
Node cur1 = head1;
Node cur2 = head2;
while (cur1 != null) {
n++;//计数长度
cur1 = cur1.next;
}
while (cur2 != null) {
n--;//head2走就要--,最后n就是长短只差
cur2 = cur2.next;
}
//(2)如果走完之后,发现,cur1 = cur2,显然就是相交的
if (cur1 != cur2) return null;//两个链表平行,不想交
//否则就是相交的
//咱需要把长链给cur1,短链给cur2
cur1 = n >= 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
//然后差值将其正数画,现在长链已经在cur1上,n必须>0
//让长链走线n,弥补差值
n = n < 0 ? -n : n;
while (n != 0){
n--;
cur1 = cur1.next;
}
//然后cur1和cur2同步走
while (cur1 != cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}
//一旦相遇,就是交点。
return cur1;
}
//给你2个链表和他们的入环节点,如果都有环,求相交交点
//又分为以下三种小情况:
//1)俩的环是独立的,压根不相交;
//2)两个相交,而且入环节点是同一个;
//3)2个链表相交,但是入环节点不相同;
public static Node bothHasLoopCross(Node head1, Node head2,
Node loop1, Node loop2){
//俩交于1一个点,提前交,或者直接在入环节点loop1=loop2那交
//2)两个相交,而且入环节点是同一个;
if (loop1 == loop2){
//用n来模拟长链和断链长度的差
int n = 0;
//(1)让cur1走head1,cur2走head2
Node cur1 = head1;
Node cur2 = head2;
while (cur1 != loop1) {//从null替换为loop1
n++;//计数长度
cur1 = cur1.next;
}
while (cur2 != loop1) {//从null替换为loop1
n--;//head2走就要--,最后n就是长短只差
cur2 = cur2.next;
}
//(2)如果走完之后,发现,cur1 = cur2,显然就是相交的
if (cur1 != cur2) return null;//两个链表平行,不相交
//否则就是相交的
//咱需要把长链给cur1,短链给cur2
cur1 = n >= 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
//然后差值将其正数画,现在长链已经在cur1上,n必须>0
//让长链走线n,弥补差值
n = n < 0 ? -n : n;
while (n != 0){
n--;
cur1 = cur1.next;
}
//然后cur1和cur2同步走
while (cur1 != cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}
//一旦相遇,就是交点。不论是交在前面,还是刚刚好交在loop1=loop2处,都是cur1
return cur1;
}else {
//1)俩的环是独立的,压根不相交;
//3)2个链表相交,但是入环节点不相同;
//求交点怎么求呢???咱们可以这么看1)3)
//设定cur1,让他们从loop1.next开始走,如果最后cur1竟然与loop2相遇,说明一定是相交的,交点也就是loop1或者loop2,随意
//如果cur1回到loop1了,一直没有遇到loop2,说明压根不相交。
Node cur1 = loop1.next;
while (cur1 != loop1){
if (cur1 == loop2) return loop2;//3)2个链表相交,但是入环节点不相同;现在找到了交点,任选其一
cur1 = cur1.next;
}
//1)俩的环是独立的,压根不相交;
return null;
}
}
好的参考文章
https://blog.csdn.net/weixin_46838716/article/details/124577412