算法通关村第一关-链表黄金挑战笔记|环的入口

解决链表环入口问题



前言

提示:无论今天过得如何,就当穿了一天的袜子,回家就脱了吧。

当然解决这个问题之前,我们需要了解一个概念,就是链表的有环是怎么判断的,定义是什么?
废话不多说,直接上图片💕

在这里插入图片描述
在这个A链表中,我们就看出他是存在环的

  1. 有环
  2. 节点 4 就是环的入口

这就引出来另一个问题了,怎么判断链表有环❔

链表中环的问题

当然拿到这个问题,思考的话(最快想到的解决方法就是Hash[map]),下来就是采用集合呗🥰,虽然这样的话可以很快解决,有点儿逃避链表的嫌疑😒

Hash和集合的解法:

遍历链表,一次将数据元素存入map中,是要是发现有相同的元素,就说明链表中有环,代码实现起来也很简单。

/**
 * 方法1:通过HashMap判断
 *
 * @param head
 * @return
 */
public static boolean hasCycleByMap(ListNode head) {
    // 一般条件下不改变head的位置
    ListNode pos = head;
    HashMap<ListNode,Integer> map = new HashMap();
    while(pos != null) {
        if(!map.containsKey(pos)){
            map.put(pos,null);
        }else{
            return true;
        }
        pos = pos.next;
    }
    return false;
}

使用集合的话,思想和上面👆类似,这里就不在重复了,直接上代码😂

/**
     * 方法1:通过HashSet判断
     *
     * @param head
     * @return
     */
    public static boolean hasCycleByMap(ListNode head) {
        // 保留一下头节点的位置
        ListNode pos = head;
        HashSet<ListNode> set = new HashSet<>();
        while (pos != null) {
        // if (set.contains(pos)){  // 这里也可以只用set判断
        //     return true;
        // }else{
        //      set.add(pos);
        // }
            if(!set.add(pos)){
                return true;
            }
            pos = pos.next;
        }
        return false;
    }

那么不逃离链表,就是使用链表需要怎么解决呢?思考(三分钟💡)
常见的算法解决 双指针 呗

快慢指针实现解决:

这里快慢指针要怎么理解呢?
我们类比一个场景:就是在一个圆形的操场上跑步,一个同学跑的快,一个同学跑的慢,经过一段时间后,必然会存在两人相遇的情况,况且正好是快的同学比慢的同学多跑一圈。
这里就可以建立数学模型

  1. 两位通过起始地点相同 快同学 v1 慢同学 v2(目前可以这样称呼 😂起名字很纠结ahhah)
  2. 经过相同的时间相遇 经过时间 t
  3. 假设围绕操场一圈的长度是 s
 s = (v1 -v2)t

这里放到链表里面就好办多了,我们就可以让fast走一步,然后fast和slow同时出发,如果后面出现fast和slow相遇,那就说明链表里面存在环呀🥰,这不就吧问题解决了。那我们就写一下代码吧。

/**
 * 方法2 通过双指针实现
 *
 * @param head
 * @return
 */

public static boolean hasCycleByTwoPoint(ListNode head) {
    // 校验参数
    if(head == null || head.next == null){ // 链表想要成环至少需要两个节点
        return false;
    }
    ListNode fast = head;
    ListNode slow = head;
    while(fast != null && fast.next != null){ // 因为需要使用到下一个节点
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow){
            return true;
        }
    }

    return false;
}

解题思路:

Hash或者使用集合的方式实现

最简单的办法就是Hash呗,当然使用集合也不是不行(Set)。这就不在赘述了,显得就有点啰嗦啦🤣

快慢指针(这里使用三次刚好解决)

解决了链表有环的问题,我们再来看看怎么确定链表的环形入口在哪里❔(思考一下💡)
我们再来看这个环的图:
在这里插入图片描述
当然如果链表是这样的话也是一样的:

在这里插入图片描述

解决思路💕:

  1. 确定链表有环 (上面我们已经解决了)
  2. 确认链表的长度(这里可以相遇的时候 固定一个fast节点 让slow继续前行直到再次相遇 就可以得出环的长度 这里我们就简单的设置为 k )
  3. 确定环的入口(当我们从链表的后面往前看的话 链表环的入口刚好是倒数第 k 个节点。

有了思路那么实现起来就比较容易了🤩,下面就是代码的展示环节了😂

/**
 * 方法2 通过双指针实现
 *
 * @param head
 * @return
 */

public static ListNode detectCycleByTwoPoint(ListNode head) {
    // 校验参数
    if (head == null || head.next == null){ // 链表想要成环至少需要两个节点
        return null;
    }
    ListNode fast = head, slow = head;
    // 确定链表有环
    while(fast != null && fast.next != null){
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow){// 说明有环
            // 确认链表的长度
            int count = 1;
            slow = slow.next;
            while (fast != slow){
                count++;
                slow = slow.next;
            }
            // 确定环的入口
            fast = head;
            slow = head;
            int temp = count;
            while(temp >0){
                fast = fast.next;
                temp--;
            }
            while (count >0){
                fast = fast.next;
                slow = slow.next;
                count--;
            }
            return slow;
        }
    }
    return null;
}

总结

提示:链表环的问题是比较经典的问题,重点理解 链表环的入口问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

师晓峰

啤酒饮料矿泉水,你的打赏冲一冲

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值