两个链表的入环节点
- 两个链表的入环节点
- 解题思路
- 代码演示
- 链表相关的题
两个链表的入环节点
给定两个可能有环也可能无环的单链表,头节点head1和head2 请实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不相交返回null 要求如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)
解题思路
这个问题,是链表中比较难的问题,我们要分成几步去思考,
首先如何判断一个链表有环无环,
这个可以用快慢指针的方式去处理,
快指针一次走两步。慢指针一次走一步,如果链表无环,快指针会率先走到结尾,也就是null;
如果有环,快指针会在环内一直循环,慢指针进环后,总会有时机和快指针相遇,根据这个情况,我们可以先判断两个链表有环无环。
找入环节点还有个技巧,当快指针和慢指针相遇后,快指针回到头节点,然后改成每次走一步,慢指针还继续每次走一步,然后再次相遇就是入环节点,可以自己证明下。
判断有环无环后,如何判断两个链表是否相交的。
这时就要讨论几种情况。如图演示
只有这三种情况,第一个两个入环节点,
第二种,一个入环节点,
第三种l两个链表都没有环
注意:不存在一个有环一个无环还能相交的情况,因为是单链表,
分别讨论以上几种情况,去计算入环节点。
第一种情况呢,两个入环节点,随便返回其中一个就可以,
第二种要找到入环节点后向前推,去找到相交的点,
第三种情况,直接找出入环节点,这个技巧在代码里讲述把
代码演示
1.先定义一个链表结构
public static class Node{
private int val;
private Node next;
public Node(int val) {
this.val = val;
}
}
- 判断一个链表是否有环,并返回入环节点
/**
* 如果一个链表有环 返回第一个入环节点,没环返回null
*
* @param head
* @return
*/
public static Node findLoop(Node head){
//无环 直接返回null
if (head == null || head.next == null || head.next.next == null){
return null;
}
Node slow = head.next;
Node fast = head.next.next;
//一个结论 如果有环的话 必会在环节点的某处位置相交.
while (slow != fast){
//有环的链表 不会走向null 如果有null 代表是无环链表
if (slow.next == null || fast.next.next == null){
return null;
}
slow = slow.next;
fast = fast.next.next;
}
//现在fast 和 slow 是相同 此时把fast放到头节点
//此时快慢节点保持相同的速度 ,就会在入环节点相交.
fast = head;
while (fast != slow){
slow = slow.next;
fast = fast.next;
}
return slow;
}
3.两个都无环时,找出相交点
/**
* 如果两个链表都是无环的.那么此时查找第一个相交的节点
* 如果相交,就像图种演示的一样,一定最后非空节点是相等的,
* @param head1
* @param head2
* @return
*/
public static Node noLoop(Node head1,Node head2){
//先证明是否相交
Node cur1 = head1;
Node cur2 = head2;
int n = 0; //链表长度
//来到最后节点并计算长度
while (cur1 != null){
n++;
cur1 = cur1.next;
}
//head2 也来到最后节点 并且计算两个链表的长度差
while (cur2 != null){
n--;
cur2 = cur2.next;
}
//如果相交 最后一个节点肯定相同的.首先判断下是否相同
if (cur1 != cur2){
//不等 代表没有相交 返回null;
return null;
}
//cur1 代表长链表 cur2 代表短链表
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;
}
- 都有环时
/**
* 都有环 也分两种情况 只有一个入环节点,和有两个入环节点
* @param head1
* @param head2
* @return
*/
public static Node bothLoop(Node head1,Node head2,Node loop1,Node loop2){
//x先把入环节点找出来
Node cur1 = head1;
Node cur2 = head2;
//入环节点相等 时,找出相交节点
//从环节点以上看,类似第一种情况都无环的求交点的方式,代码也一样.
if (loop1 == loop2){
//肯定相交
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{
//有可能相交 也有可能不相交
//在换上转圈 如果能遇到loop2 就是相交,遇不到 就是不相交
cur1 = loop1.next;
while (cur1 != loop1){
if (cur1 == loop2){
return cur1;
}
cur1 = cur1.next;
}
return null;
}
}
5,方法组合起来就是最终答案
/**
* 相交节点
* @param head1
* @param head2
* @return
*/
public static Node findIntersectNode(Node head1,Node head2){
//head1 的入环节点
Node loop1 = findLoop(head1);
//head2 的入环节点
Node loop2 = findLoop(head2);
//无环时
if (loop1 == null && loop2 == null){
return noLoop(head1,head2);
}
//都有环时
if (loop1 != null && loop2 != null){
return bothLoop(head1,head2,loop1,loop2);
}
return null;
}
链表相关的题
leetcode 二叉树展开为链表
递归实现翻转链表
删除排序链表中的重复元素
leetcode143. 重排链表
leetcode109. 有序链表转换二叉搜索树
leetcode61. 旋转链表