LeetCode141-Linked List Cycle & 142-Linked List Cycle II & 160-Intersection Of Two Linked Lists

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxzxzx0119/article/details/83061653

LeetCode141-Linked List Cycle & 142-Linked List Cycle II & 160-Intersection Of Two Linked Lists

  • LeetCode-141 Linked List Cycle(判断是否有环)
  • LeetCode-142 Linked List Cycle II(找到第一个入环节点)
  • LeetCode-160 Intersection Of Two Linked Lists(找到两个链表的第一个相交的节点(两个链表都无环))
  • 扩展问题(两个链表可以有环的情况下找到两个链表的第一个相交的节点)

LeetCode141 Linked List Cycle(判断是否有环)

题目链接

题目

在这里插入图片描述

解析

第一种做法: 使用HashSet记录已经走过的节点,如果再次碰到,则有环:

public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null)
            return false;
        HashSet<ListNode>set = new HashSet<>();
        
        ListNode cur = head;
        while(cur != null){
            if(set.contains(cur))
                return true;
            set.add(cur);
            cur = cur.next;
        }
        
        return false;
    }
}

题目要求不能使用额外的空间,所以第二种解法:

  • 使用两个指针,一个快指针fast一次走两步,慢指针一次走一步;
  • 如果快指针fast某个时刻走到空了,说明没有环, 因为如果有环,快指针一定不会走到空(慢指针也不会(因为是单链表));
  • 所以如果fast没有走到空,那快指针fast和慢指针slow就会在环里面打圈,因为fast更快,所以一定会追上slow,当fast == slow的时候就返回true就可以了;

在这里插入图片描述

public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null)
            return false;
        ListNode slow = head;
        ListNode fast = head;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow)
                return true;
        }
        return false;
    }
}

LeetCode142 Linked List Cycle II(找到第一个入环节点)

题目链接

题目

在这里插入图片描述

解析

同样可以使用HashSet,但是也不符合题目使用O(1)空间的要求。
O(1)空间的解法还是使用快慢指针,唯一的不同就是

  • 他们相遇之后,让fast回到head(头结点),然后两个指针每次都只走一步,他们一定会相遇,而且相遇的节点就是第一个入环节点

按照上面的思路写出代码很简单,关键是怎么证明就是那样走?

看下图的解释,需要证明的是 边a == 边c,即最后可以fast和slow一起一步一步的走。。。
在这里插入图片描述

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null)
            return null;
        ListNode fast = head;
        ListNode slow = head;
        
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){//当fast和slow相交的时,让fast回到起点,fast和slow都只走一步,然后fast和slow第一次相遇的地方就是交点
                fast = head;
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                return fast;
            }
        }
        return null;
    }
}



LeetCode160 Intersection Of Two Linked Lists(找到两个链表的第一个相交的节点(两个链表都无环))

题目链接

题目

在这里插入图片描述

解析

一个比较好的解法是:

  • 先统计两个链表的长度lenA和lenB(都先遍历一次);
  • 然后判断两个链表各自走到最后的指针end1和end2是否相等,如果不相等,直接返回false;
  • 然后如果lenA > lenB,则B链表的指针先走lenA - lenB步,然后两个指针一起走,第一个相等的节点就是相交的节点;
  • 同理lenB > lenA,则A链表的指针先走lenB - lenA步,然后一起走。。。
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null)
            return null;
        
        //先求出两个链表的长度
        int lenA = 1;
        ListNode cur1 = headA;
        while(cur1.next != null){
            lenA++;
            cur1 = cur1.next;
        }
        int lenB = 1;
        ListNode cur2 = headB;
        while(cur2.next != null){
            lenB++;
            cur2 = cur2.next;
        }
        //最后都不相等,肯定不相交
        if(cur1 != cur2)
            return null;
        
        // cur1是长的,cur2是短的
        cur1 = lenA > lenB ? headA : headB;
        cur2 = cur1 == headA ? headB : headA;
        int n = Math.abs(lenA - lenB);
        
        //cur1先走 abs(lenA - lenB步)
        while(n > 0){
            n--;
            cur1 = cur1.next;
        }
        
        //一起走,第一次相遇就是交点
        while(cur1 != cur2){
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }
}

扩展问题(两个链表可以有环的情况下找到两个链表的第一个相交的节点)

问题:

两个链表,可以有环或者无环,可以相交或者不相交,如果相交,返回第一个入环节点,否则返回null。

扫描二维码关注公众号,回复: 3605022 查看本文章

解析

有三种情况:

  • 一个链表有环,另一个链表无环,这种情况两个链表不可能相交(不论怎么画),直接返回null;
  • 两个链表都无环,就是LeetCode160的解法;
  • 第三种情况: 两个链表有环,这种情况又可以分为三种情况。

第三种情况的三种情况,假设链表1的第一个入环节点记为loop1,链表2的第一个入环节点记为loop2:

  • 如果loop1 == loop2,则两个链表结构如图(一)所示,这时我们只要考虑head1到loop1和head2到loop2的部分也就是图中红色阴影的部分,这个又是LeetCode160的处理方式,只不过改了结束的位置;
  • 如果loop1 != loop2,则又分两种情况,第一种如图(二),第二种如图三,区别这两种方式的方法看下图解释;

在这里插入图片描述

代码如下:

public class Solution {
     //主过程
    public ListNode getIntersectionNode(ListNode head1, ListNode head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        ListNode loop1 = detectCycle(head1);
        ListNode loop2 = detectCycle(head2);
        if (loop1 == null && loop2 == null) {
            return getIntersectionNodeNoLoop(head1, head2); //两个都无环的处理方式
        }
        if (loop1 != null && loop2 != null) {
            return bothLoop(head1, loop1, head2, loop2);  //两个都有环的处理
        }
        return null;  // 一个有环一个无环
    }


    //找到某个链表第一个入环节点
    public ListNode detectCycle(ListNode head) {
        if(head == null)
            return null;
        ListNode fast = head;
        ListNode slow = head;

        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){//当fast和slow相交的时,让fast回到起点,fast和slow都只走一步,然后fast和slow第一次相遇的地方就是交点
                fast = head;
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                return fast;
            }
        }
        return null;
    }

    public ListNode getIntersectionNodeNoLoop(ListNode headA, ListNode headB) {
        if(headA == null || headB == null)
            return null;

        //先求出两个链表的长度
        int lenA = 1;
        ListNode cur1 = headA;
        while(cur1.next != null){
            lenA++;
            cur1 = cur1.next;
        }
        int lenB = 1;
        ListNode cur2 = headB;
        while(cur2.next != null){
            lenB++;
            cur2 = cur2.next;
        }
        //最后都不相等,肯定不相交
        if(cur1 != cur2)
            return null;

        // cur1是长的,cur2是短的
        cur1 = lenA > lenB ? headA : headB;
        cur2 = cur1 == headA ? headB : headA;
        int n = Math.abs(lenA - lenB);

        //cur1先走 abs(lenA - lenB步)
        while(n > 0){
            n--;
            cur1 = cur1.next;
        }

        //一起走,第一次相遇就是交点
        while(cur1 != cur2){
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }

    //两个都有环的处理
    public ListNode bothLoop(ListNode head1, ListNode loop1, ListNode head2, ListNode loop2) {
        ListNode cur1 = null;
        ListNode cur2 = null;

        if (loop1 == loop2) { // 图(一)情况 类似无环的处理 LeetCode 160  这里和上面处理稍有点不同
            cur1 = head1;
            cur2 = head2;
            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 {  //图(三) 的情况
            cur1 = loop1.next;
            while (cur1 != loop1) {
                if (cur1 == loop2) {
                    return loop1;
                }
                cur1 = cur1.next;
            }
            return null;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/zxzxzx0119/article/details/83061653
今日推荐