两个单链表相交的一系列问题
单链表可能有环,也可能无环。给定两个单链表的头节点 head1 和 head2, 这两个链表可能相交,也可能不相交。
请实现一个函数,如果两个链表相交,请返回相交 的第一个节点;如果不相交,返回 null 即可。
要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到O(N+M),额外空间复杂度请达到O(1)。
思路
本题目可以拆分为2大问题
然后又可以拆分为多个子问题
- 判断单链表是否有环,如果有找出入环点
- 两个无环链表是否相交
- 两个有环链表是否相交
- 一个有环链表和一个无环链表是否相交
判断单链表是否有环,如果有找出入环点
- 快指针一次走两步,慢指针一次走一步,直到快慢指针相遇
- 新指针从链表头开始走,慢指针从刚刚位置继续走
- 新指针和慢指针相遇的地方就是入环点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| // 判断链表是否有环 public static Node getLoopNode(Node head) { if(head == null || head.next ==null || head.next.next ==null){ return null; } Node nq = head; Node ns = head.next.next; //循环使得快慢指针相遇 while (ns != nq) { if(ns == null || nq == null){ break; } ns = ns.next; nq = nq.next.next; } Node node = head; //新指针和慢指针遍历,直到相遇 while (ns != node) { if(ns == null || node == null){ return null; } node = node.next; ns = ns.next; } return node; }
|
两个无环链表是否相交
如果两个没有环的链表相交于某一节点,那么在这个节点之后的所有节点都是两个链表共有的
因此这两个链表的最后一个节点一定是公共的
所以先遍历两个链表,判断末尾节点是否是同一个,同时设置一个计数,表示两个链表长度的差值
然后让长的链表先从头走过这个差值,然后长短一起走,直到发现第一个公共节点
这个长度差值还可以体现在求链表倒数第k个结点。
即有2个指针指向链表,一个先走k步,然后两者一起走,直到有一个为空为止。
这样非空的指针指向的就是倒数k的节点
因为两者一个都相差k步,所有快的走完全程,慢的就停在了倒数k步上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| // 无环的时候找公共的节点 public static Node noLoop(Node head1, Node head2) { // n是记录长链表比短链表长多少,然后让长链表走过这一段,之后一起走,找到公共节点 int n = 0; // head1走完全程 Node n1 = head1; while (n1.next != null) { n++; n1 = n1.next; } // head2走完全程 Node n2 = head2; while (n2.next != null) { n--; n2 = n2.next; } // 最后一个节点不相同,则一定没有公共节点 if (n1 != n2) { return null; } // 把n1设置为长度更长的链表 n1 = n > 0 ? head1 : head2; n2 = n1 == head1 ? head2 : head1; // n1把多出来的部分走过 n = Math.abs(n); while (n != 0) { n--; n1 = n1.next; } // n1==n2时就是公共节点 while (n1 != n2) { n1 = n1.next; n2 = n2.next; } return n1; }
|
两个有环链表是否相交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) { if (loop1 == loop2) { // 入环点相同,相交点在环外 // 方法类似无环链表求公共点 // 先求长度差,然后长的走过长度差,之后一起走,直到相遇 int n = 0; Node n1 = head1; //遍历到入环点停止,而不是为空 while (n1.next != loop1) { n++; n1 = n1.next; } Node n2 = head2; //遍历到入环点停止,而不是为空 while (n2.next != loop2) { n--; n2 = n2.next; } n1 = n > 0 ? head1 : head2; n2 = n1 == head1 ? head2 : head1; n = Math.abs(n); //长的走过多余的部分 while (n != 0) { n--; n1 = n1.next; } //一起走,直到相遇 while (n1 != n2) { n1 = n1.next; n2 = n2.next; } return n1; } else { // 入环点不同,只要遍历一个环,检查另一个入环点是否在其中即可 Node node = loop1; do { // loop2是否在loop1中 if (node == loop2) { return node; } node = node.next; } while (node != loop1); // 不在其中,则不相交 return null; } }
|
一个有环链表和一个无环链表是否相交
一个有环链表和一个无环链表不可能相交
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static Node getIntersectNode(Node head1, Node head2) { if (head1 == null || head2 == null) { return null; } Node loop1 = getLoopNode(head1); Node loop2 = getLoopNode(head2); // 12都没有环 if (loop1 == null && loop2 == null) { return noLoop(head1, head2); } // 12都有环 if (loop1 != null && loop2 != null) { return bothLoop(head1, loop1, head2, loop2); } // 一个有环一个无环,不可能相交! return null; }
|