两个单链表相交问题(有环、无环)

题目描述:给定两个可能有环也可能无环的单链表,头节点head1和head2,请实现一个函数,如果两个链表相交,返回相交的第一个节点,如果不相交,返回 null 。

 

目录

要解决的第一个问题,判断链表是否有环

方法一:哈希表

方法二:快慢指针

两个链表都没环代码实现:

两个链表都有环代码实现:

最终代码:


要解决的第一个问题,判断链表是否有环

方法一:哈希表

时间复杂度 O(N) ,空间复杂度 O( N )

public ListNode isLoop(ListNode head){

    HashSet set = new HashSet();
    
    ListNode p = head;
    while(p != null){
        if(set.contains(p)){
            return p;
        }else{
            set.add(p);
        }
        p = p.next;
    }

    return null;


}

方法二:快慢指针

    力扣官方证明:

疑点记录:

为什么 fast 和 slow 指针一定会相遇,而不是路过呢?

        fast 指针一次走两步, slow 指针一次走一步,所以 fast 指针的追赶速度就是 一次一步。追赶速度并没有跳步,所以不会路过。

为什么在 slow 指针入环的第一圈就会相遇呢?

        假设环长度为 a

        把入环节点当作起点,slow 位于起点,最坏情况:如果此时 fast 指针在走了 n 圈之后来到了起点的下一个节点,这时追赶距离为 a - 1,因为追赶速度为 1,所以追赶时间 为 a - 1,也就是在 a - 1 时间后,fast 指针追上了 slow 指针,而 slow 指针一次走一步,在 a - 1 的时间里,slow 指针也走了 a - 1 步,不足以圈,所以在 slow 指针入环的第一圈就会相遇

代码:

public ListNode isLoop(ListNode head){
    if(head == null || head .next == null || head.next.next == null){
        return null;

    }
    ListNode fast = head;
    ListNode slow = head;
    while(fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
         if(fast == slow){
            break;
        }
    }
        
    if(fast == null || fast.next == null){

        return null;//说明无环    
    }
    
    //有环,将 fast 指针置于头部,和 slow 指针每次都走一步,相遇时即为入环节点
    fast = head;
    while(fast != slow){
        fast = fast.next;
        slow = slow.next;
    }
    return fast;
}

 遇到的坑:

上面代码我们时将 head 节点作为 fast 和 slow 的共同出发点,在第一次相遇后将 fast 节点置于出发点 head。

但如果我们将 head 的前一个节点(虚拟节点)作为出发点,初始化为如下:

ListNode fast = head.next;
ListNode slow = head;

这时我们在将 fast 节点重新置于出发点时就不能置于 head 节点了,fast 节点我们重新给的位置是共同的出发节点,而我们初始时的出发节点是 head 的前一个节点(虚拟节点),所以我们要将 fast 指向虚拟节点。

如下:

public ListNode isLoop(ListNode head){
     if(head == null || head .next == null || head.next.next == null){
        return null;

    }
    ListNode fast = head.next;
    ListNode slow = head;
    while(fast != null && fast.next != null) {
          if(fast == slow){
            break;
        }
        fast = fast.next.next;
        slow = slow.next;
       
    }
        
    if(fast == null || fast.next == null){

        return null;//说明无环    
    }
    
    //有环,将 fast 指针置于头部,和 slow 指针每次都走一步,相遇时即为入环节点
    //定义虚拟节点pre
    ListNode pre = new ListNode();
    if(fast == slow){
        pre.next = head;
        while(pre != slow){
            pre = pre.next;
            slow = slow.next;
        }
    }
    return pre;

}


解决完是否有环后,就能处理相交问题了

  •  两个链表都没环

两个链表都没环代码实现:

public ListNode noLoop(ListNode headA,ListNode headB){
    int n = 0;

    ListNode p1 = headA;
    ListNode p2 = headB;
    while(p1 != null){
        n++;
        p1 = p1.next;
    }
    while(p2 != null){
        n--;
        p2 = p2.next;
    }

    // n 就是headA 的长度 减去 headB 的长度
    ListNode cur = n > 0 ? headA : headB;
    
    ListNode other = cur == headA ? headB : headA;
    n = Math.abs(n);
    while(n-- > 0){
        cur = cur.next;
    }

    while( cur != null){
        if(cur == other){
            break;
        }    
        cur = cur.next;
        other = other.next;
    }
    
    return cur;
}
  • 一个有环,一个没环( 这种情况不可能相交 )
  • 两个链表都有环

 情况二逻辑与  noLoop 相似

 两个链表都有环代码实现:

public ListNode bothLoop(ListNode headA,ListNode loop1,ListNode headB,ListNode loop2){
    ListNode p1 = headA;
    ListNode p2 = headB;

    if(loop1 == loop2){    
        int n = 0;
        while(p1 != loop1){
            n++;
            p1 = p1.next;
        }

        while(p2 != loop2){
            n--;
            p2 = p2.next;
        }

        ListNode cur = n > 0 ? headA : headB;
        ListNode other = cur == headA ? headB : headA;

        while(n-- > 0){
            cur = cur.next;
        }
        
        while(cur != other){
            cur = cur.next;
            other = other.next;
        }
        return cur;
    }else{
        p1 = loop1.next;
        while(p1 != loop1){
            if(p1 == loop2){
                return p1;
            }
            p1 = p1.next;
        }
        return null;
    }
}

最终代码:

public ListNode getIntersectNode(ListNode head1,ListNode head2){
    if(head1 == null || head2 == null){
        return null;
    }
    
    ListNode loop1 = isLoop(head1);
    ListNode loop2 = isLoop(head2);
    //两个都没环
    if(loop1 == null && loop2 == null){
        return noloop(head1,head2);
    }
    //两个都有环
    if(loop1 != null && loop2 != null){
        return bothLoop(head1,loop1,head2,loop2);
    }
    //其他情况不可能相交
    return null;


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值