leetcode141-环形链表

这篇博客探讨了如何判断链表中是否存在环,提供了四种不同的解决方案。其中包括通过修改节点值、使用HashSet存储已遍历节点、采用快慢指针法以及反转链表后再比较的方法。每种方法的时间复杂度和空间复杂度都有所不同,如快慢指针法在保持常量空间复杂度的同时实现了高效判断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.题目描述

给定一个链表,判断链表中是否有环。

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

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

进阶:

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

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

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

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

提示:

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

二.题目解析

public boolean hasCycle(ListNode head) {
        /*一次遍历,修改遍历过的节点值为Integer.MIN_VALUE,若还将遍历到这个值,证明有环
        时间复杂度O(n),空间复杂度O(1),缺点:修改了链表
        * */
        int min = Integer.MIN_VALUE;
        ListNode cur = head;
        while (cur != null){
            //这个标记再次出现,意味这个结点被访问过,说明链表循环
            if(cur.val == min){
                return true;
            }
            //给此正在遍历的节点赋值
            cur.val = min;
            //遍历下一节点
            cur = cur.next;
        }
        return false;
    }

在这里插入图片描述

public boolean hasCycle1(ListNode head) {
        /*hashset保存遍历过的节点
        时间复杂度O(n),空间复杂度O(n)
        * */
        HashSet<ListNode> listSet = new HashSet<>();
        ListNode p = head;
        while(true) {
            if(p == null) {
                return false;
            }
            if(listSet.contains(p)) {
                return true;
            }
            listSet.add(p);
            p = p.next;
        }
    }

在这里插入图片描述

public boolean hasCycle2(ListNode head) {
        /*快慢指针法,当快慢指针速度差为1的时候,必会在环内相遇。
        当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,
        同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会和慢指针相遇
        时间复杂度O(n),空间复杂度O(1)
        * */
        ListNode fast = head,slow = head;
        while (fast != null && fast.next != null){
            //快慢指针按照不同的速度移动
            fast = fast.next.next;
            slow = slow.next;
            //若相遇则有环,这个判断语句要放在移动语句后面(初始first == slow)
            if(fast == slow){
                return true;
            }
        }
        return false;
    }

在这里插入图片描述

public boolean hasCycle3(ListNode head) {
        /*先反转再比较,如果有环,那么链表反转之后,
        原来的头结点和反转之后的头结点一定是同一个
        时间复杂度O(n),空间复杂度O(1)
        * */
       ListNode reverseHead = reverse(head);
       //必须是链表不为空,且非只有一个节点,且reverseHead == head才是有环链表
        //如果只有一个节点也满足reverseHead == head,但是却是无环的
       if(head != null && head.next != null && reverseHead == head){
           return true;
       }
       return false;
    }
    public ListNode reverse(ListNode head){
        /*反转链表,如果存在环,那么最终cur == null,退出循环,将返回pre即1
        1->2->3->4               |------
           ↑      |     ->       |      ↓
           |------|           1->2<-3<-4
        * */
        ListNode pre = null;
        ListNode cur = head,nextCur = null;
        while (cur != null){
            //记录下个处理的节点
            nextCur = cur.next;
            //实现反转
            cur.next = pre;
            //更新反转节点前面的那个节点
            pre = cur;
            //更新要处理的节点
            cur = nextCur;
        }
        return pre;
    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值