LeetCode141-142环形链1、2⭐️

环形链表1--判断链表是否有环

题目解析

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos-1 或者链表中的一个 有效索引

进阶:你能用 O(1)(即,常量)内存解决此问题吗?


思路

思路1:哈希表法,遍历链表,把节点存入set,如果有环,那么环遍历一遍后,最终肯定会存在于set中。

思路2:弗洛伊德龟兔赛跑法,

假想「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方;如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。

我们可以根据上述思路来解决本题。具体地,我们定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。

代码

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

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        //head指向null或者链表只有一个元素,直接返回false
        if(head==null||head.next==null){
            return false;
        }
        //初始化两个快慢指针,慢的一次移动一下,快的一次移动两下,如果有环,肯定会相等
        ListNode slow=head;
        ListNode fast = head.next.next;

        while(slow!=fast&&fast!=null&&slow!=null){
            slow=current.next;
            //需要确保fast.next不能为null
            if(fast.next!=null){
                fast = fast.next.next;
            }else{
                fast=null;
            }
            
        }

        return fast!=null;

    }
}

笑话

环形链表2--找到入环点

题目解析

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

进阶:你是否可以使用 O(1) 空间解决此题?

思路

思路一

同找环的方法哈希表法,遍历链表,把节点存入set,如果有环,那么环遍历一遍后,最终肯定会存在于set中,第一个在set中的就是链表开始入环的第一个节点

思路二

仍然使用弗洛伊德龟兔赛跑法

设链表中环外部分的长度为 a。slow 指针进入环后,又走了 b 的距离与 fast 相遇。此时,fast 指针已经走完了环的 n 圈,因此它走过的总距离为 a+n(b+c)+b=a+(n+1)b+nc。

有了 a=c+(n−1)(b+c) 的等量关系,我们会发现:从相遇点到入环点的距离c加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离a。

因此,当发现 slow 与 fast 相遇时,我们再使用一个指针 ptr。起始,它指向链表头部;随后,它和 slow 每次向后移动一个位置。最终,它们会在入环点相遇。

上边的解释意思是:当fast和slow在环内相遇时,此时再新增一个指针ptr指向链表头,然后一次移动一步,slow也从相遇点一次移动一步在环内移动,根据公式a=c+(n−1)(b+c),最终一定是在入环点相遇,此时可能是slow在环内转了一圈+c的距离,此时a=c

也可能是转了多圈,总之会在入环点相遇

代码

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode current = head;
        Set<ListNode> set = new HashSet<>();
        while(current!=null){
            if(set.contains(current)){
               return current;
            }else{
                set.add(current);
                current=current.next;
            }
        }
        return null;
    }
}

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        //排除头结点为空场景
        if(head==null){
            return null;
        }
        //定义slow,fast都指向头指针
        ListNode  slow=head;
        ListNode fast=head;
        //fast=null说明没环之间返回null
        while(fast!=null){
            //fast.next!=null说明可以继续进行循环下去
            if(fast.next!=null){
                //慢指针一次移动一下,快指针一次移动两下
                slow=slow.next;
                fast=fast.next.next;
            }else{
                //fast.next=null,说明没环
                return null;
            }
            //当有环时
            if(fast==slow){
                //定义新指针指向头
                ListNode ptr=head;
                //没相遇
                while(ptr!=slow){
                    //ptr,slow都移动一步
                    ptr=ptr.next;
                    slow=slow.next;
                }
                //相遇点就是入环点
                return ptr;
            }
        }
        return null;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值