算法练习(10)-求2个(可能有环的)单链表的相交节点
注:题解来自左程云大佬,记录一下
这个问题可以看做是 算法练习(7)-判断单链表是否有环,以及求环的长度 的升级版,分析:对于单链表而言,如果2个链表能相交,只可能出现下面这几种情况
| 链表1 类型 | 链表2 类型 | 相交可能性? | 备注 | 图例 |
| 无环 | 无环 | √ | ![]() |
|
| 无环 | 有环 | × | 如果能相交,必然2个都是有环 | |
| 有环 | 有环 | √ | 或 |
可以分解成几个小问题:
1、如何判断链表是有环的?
/**
* 如果1个单链表有环,返回入环节点
*
* @param head
* @return 如果有环, 则返回入环节点,否则返回null
*/
public static LinkNode getLoopEntrance(LinkNode head) {
if (head == null || head.next == null) {
return null;
}
LinkNode slow = head;
LinkNode fast = head;
boolean isLoop = false;
//先跑一圈, 判断是否有环
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow.equals(fast)) {
isLoop = true;
break;
}
}
if (!isLoop) {
return null;
}
//快指针重回头部,再跟慢指针齐步向前走,fast与slow必然相交于入环点(数学上可证明,证明过程略)
fast = head;
while (fast.next != null) {
if (slow.equals(fast)) {
return slow;
}
slow = slow.next;
fast = fast.next;
}
return null;
}
2、如何判断2个无环链表,是否相交?
/**
* 返回2个无环链表的相交节点
*
* @param head1 链表1的头节点
* @param head2 链表2的头节点
* @return 如果相交,则返回相交节点,否则返回null
*/
public static LinkNode getNoLoopCrossNode(LinkNode head1, LinkNode head2) {
//思路:先测量各自的长度, 然后找出长度差值
//第二轮遍历时, 让长度大的链表先走差值步,再2个链表齐步走, 如果有相交,必然在交叉点相遇
if (head1 == null || head2 == null) {
return null;
}
int size1 = 0, size2 = 0;
LinkNode h1 = head1, h2 = head2;
while (h1 != null) {
size1 += 1;
h1 = h1.next;
}
while (h2 != null) {
size2 += 1;
h2 = h2.next;
}
int diff = Math.abs(size1 - size2);
//让h1指向长的链表头
h1 = size1 >= size2 ? head1 : head2;
//让h2指向短的链表头
h2 = h1.equals(head2) ? head1 : head2;
//长链表先走差值步
for (int i = 0; i < diff; i++) {
h1 = h1.next;
}
//2个链表再齐步走
while (h1 != null && h2 != null) {
if (h1.equals(h2)) {
return h1;
}
h1 = h1.next;
h2 = h2.next;
}
return null;
}
3、如何判断2个有环链表,是否相交?
/**
* 判断2个有环单链表是否相交
*
* @param entrance1 链表1的入环节点
* @param entrance2 链表2的入环节点
* @return 如果相交, 返回相交点
*/
public static LinkNode getLoopCrossNode(LinkNode entrance1, LinkNode entrance2) {
if (entrance1 == null || entrance2 == null) {
return null;
}
//2个入环节点相同, 必然相交
if (entrance1.equals(entrance2)) {
return entrance1;
}
//从入环节点a出发, 一直向前走, 直到再次遇到自己前, 如果路上遇到另1个入环节点b,则a,b肯定在一个环上
LinkNode n1 = entrance1;
while (entrance1 != null) {
if (entrance1.equals(entrance2)) {
return entrance1;
}
entrance1 = entrance1.next;
if (n1.equals(entrance1)) {
break;
}
}
return null;
}
综合以上几个小方法, 就能解决该问题:
/**
* 如果2个(可能有环的)链表相交,返回相交点
* @param h1 链表1的头节点
* @param h2 链表2的头节点
* @return 如果相交,返回相交节点
*/
public static LinkNode getCrossNode(LinkNode h1, LinkNode h2) {
LinkNode entrance1 = getLoopEntrance(h1);
LinkNode entrance2 = getLoopEntrance(h2);
if (entrance1 == null && entrance2 == null) {
//都是无环链表
return getNoLoopCrossNode(h1, h2);
}
//至少1个是有环链表
return getLoopCrossNode(entrance1, entrance2);
}
作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
这个问题可以看做是 算法练习(7)-判断单链表是否有环,以及求环的长度 的升级版

或
浙公网安备 33010602011771号