题目:判断两个单链表的是否相交的,如果存在输出第一个相交的节点,否则输出null
该问题可以差分为三个子问题:
- 判断一个单链表是否存在环,有环则相交。
- 判断两个无环单链表是否相交。
- 判断两个有环单链表是否相交。
1.判断一个单链表是否存在环
1)最简单的方法,使用HashSet,一边遍历将节点放到Set中,一边检查Set中节点是否重复,如果发现重复直接返回,该方法使用了额外的空间。
import java.util.HashSet; import java.util.Set; public class NodeDemo { public static class Node{ public int value; public Node next; public Node(int data){ this.value = data; } } public static Node getLoopNode(Node node){ Set<Node> set = new HashSet<>(); //用来保存节点,检查是否有重复出现的节点,如果有就是相交的节点,直接返回 Node cur = node; while(cur != null){ if (!set.contains(cur)) { set.add(cur); }else { return cur; } cur = cur.next; } return null; } public static void main(String[] args) { Node node = new Node(1); Node head = node; node.next = new Node(2); node.next.next = new Node(3); node.next.next.next = new Node(4); node.next.next.next.next = new Node(5); node.next.next.next.next.next = new Node(6); node.next.next.next.next.next.next = head.next.next; Node node1 = getLoopNode(node); System.out.println(node1.value); //输出重复节点的值 } }
2)不使用额外空间的方法:
- 准备两个指针,一个快指针Fast,一个慢指针Slow,分别指向头结点。快指针每一次走两个节点,慢指针每一次走一个节点。
- 直到快指针和慢指针相遇的时候,快指针重新指向头结点,然后每一次走一个节点。
- 直到快慢指针相遇,相遇的节点就是第一个相交节点。
-
public static Node getLoopNode(Node head) { if (head == null || head.next == null || head.next.next == null) { return null; } Node n1 = head.next; //慢指针 Node n2 = head.next.next; //快指针 while (n1 != n2) { if (n2.next == null || n2.next.next == null) { return null; } n2 = n2.next.next; n1 = n1.next; } n2 = head; //快指针重新指向头节点 while (n1 != n2) { n1 = n1.next; n2 = n2.next; } return n1; }
2.判断两个无环链表是否相交。
判断条件:如果两个无环链表有相同的尾节点,则相交,如果没有则不相交。
示意图:
- 使用两个指针分别指向两个链表的头部,分别遍历一遍,得出两个链表的长度差n。
- 两个指针重新指向头节点,长度长的链表指针先走长度差n的距离。
- 然后和另一个链表指针一起指向下一个节点,直到相遇。
示意图:
public static class Node{ public int value; public Node next; public Node(int data){ this.value = data; } } public 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) //最后一个节点不相等,说明两个链表不相交,直接返回null return null; cur1 = n > 0 ? head1 : head2; //cur1总是指向两个链表中长的那一个 cur2 = cur1 == head1 ? head2: head1; n = Math.abs(n); while (n != 0) { n--; cur1 = cur1.next; } while (cur1 != cur2) { //当cur1和cur2相交的时候停止 cur1 = cur1.next; cur2 = cur2.next; } return cur1; }
3.判断两个有环链表是否相交。
两个有环链表有三种情况:
第一种:
第二种:
第三种:
- 第一种情况和无环链表相交问题类似,只是将环的入口当做无环链表的最后一个节点。
- 第二种情况环的入口有两个,所以相交的节点有两个答案,使用一个指针在环入口循环如果遇到另一个环入口,则属于第二种情况,否则属于第三种情况。
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; } }
注:一个链表有环,一个链表无环不可能存在相交问题。