两个链表的入环节点
- 两个链表的入环节点
- 解题思路
- 代码演示
 
- 链表相关的题
两个链表的入环节点
给定两个可能有环也可能无环的单链表,头节点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. 旋转链表



















