橘子刷题第八题之判断是否环形链表

题目:本题出自力扣第一百四十一题,判断链表里面是否有环
给你一个链表的头节点 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:判断有无,我选哈希

分析一下,判断链表里面是不是有环,首先就是看看链表里面是不是最后走到以前走过的地方去了,这就是环。所以其实是一个判断有无的问题。那你就是上哈希表吧,你遍历这个链表,遍历一个就往hash里面放一下,后面遍历每次放的时候就判断一次哈希表里面有没有,要是有那就是以前放过了,那就是环了,要是没有最后那就退出就是个直线链表。返回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) {
     	// 安全校验
        if(head == null) return false;
        // 声明一个map出来
        Map <ListNode,ListNode>map = new HashMap<>();
        // 游标指针遍历
        ListNode index = head;
        // 遍历
        while(index != null){
            // 如果以前map里面放过了,那就是说明你有环,又走到这里了,直接返回true
            if(map.get(index) != null){
                return true;
            }
            // 没有就继续往进放
            map.put(index,index);
            index = index.next;
        }
        // 最后要是直线链表,跳出返回false
        return false;
    }
}

在这里插入图片描述
我们看到他的空间效率其实不好,因为我们引入了一个hash表来处理,而且题目进阶要求你能不能用O(1)空间复杂度来处理。
所以需要优化。

解法2:转化思路,追击问题

其实开始我看到这个环的时候,我第一反应就是mysql中的redo log日志。他也是个环,就是他处理的就是一个前面写后面追的一个check point的机制,所以我想着也是这样。
其实换个角度,环状你看像不像数据在链表上走,最后一定会回到原来的位置,像是一个追赶的样子,就像当初大学体侧那个傻逼超了我一圈,最后走到一个路上的样子。
所以其实这里有个思路,就是双指针(YYDS)。但是这个双指针不一样,最好是一个块一个慢,如果有环,快的一定能追上慢的,慢的和快的一起走。你可能会问了,既然有环,为啥慢的还要走,快的走追上就行了。注意,是有环,不是一开始就是个环,环可能在后面,所以你慢的也要走。
那么我们就申明两个指针,一个每次走一个位置,一个走两个,要是最后快的追上了慢的,那就有环。
你可能又会问了,为啥一个走一个,一个走两个呢?其实你只要一个快一个慢就行了,因为只要速度不一样,一定会追上的。你要是一个走五个,一个走两个,也会追上,但是可能会多跑几圈,计算量增大。
有了思路那就来代码了。

/**
 * 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) {
        // 安全校验
        if(head == null) return false;
        // 声明快慢指针
        ListNode fastNode = head,slowNode = head;
        // 判断快指针的下一个和下下个,因为快指针在前面跑,所以拿他判断,而且他要走两个,所以你最好后面和后面的后面都判断一下
        // 要是后面没了,那后面的后面也就没必要了
        while(fastNode.next != null && fastNode.next.next != null){
            // 慢指针前进一个
            slowNode = slowNode.next;
            // 快指针前进两个
            fastNode = fastNode.next.next;
            // 出现环
            if(slowNode == fastNode) return true;
        }

        // 跳出返回false
        return false;
    }
}

在这里插入图片描述

总结

环形问题,也是个双指针,所有这种最后回到一起的都是相遇问题。可以以快慢双指针来处理。
世间所有的相遇都是久别重逢。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值