文章目录

  • 前言
  • 正文
    • 原题:
    • 思路1:
    • 思路2
    • 思路3
  • 总结

前言

今天这道题目的第一种解法很奇葩,用计时器竟然可以AC,并且可以自己调整时间多少,跟我一起来看看吧。


正文

原题:

链接:环形链表

Given a linked list, determine if it has a cycle in it.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list.
Example 1:
Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the second node.

Example 2:
Input: head = [1,2], pos = 0
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the first node.

Example 3:
Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.

Follow up:
Can you solve it using O(1) (i.e. constant) memory?

题目大意
给定一个链表,判断是不是环形链表,没错就是这么简单。

思路1:

一般我们遍历链表(非环形链表)的时候,直接上来一个while (head != null),然后循环体里面head = head.next,类似下面的语句

while (head != null) {head = head.next;
}

但是环形链表就不行了,可能会遇到无限循环导致超时的问题,这时就产生了第一种思路。
既然环形链表会超时,那我们把时间限制在某个范围之间,比如0.1s后还在运行的话,那可能就是存在环形链表了;可以使用Java自带的System.currentTimeMillis()方法获取当前时间,当然别的语言也同样提供了类似的方法,通过以下代码就可以计算出代码运行时间:

// 过去的时间
long oldTime = System.currentTimeMillis();
while (true) {// 这里是循环体代码
}
// 当前时间
long curTime = System.currentTimeMillis();
// 代码运行时间等于curTime - oldTime

curTime - oldTime就是循环体运行的时间了,我们来看一下思路1的完整代码:

代码

public class Solution {public boolean hasCycle(ListNode head) {// 如果超过某个时间,比如0.1s,则说明有环long oldTime = System.currentTimeMillis();while (head != null) {head = head.next;long curTime = System.currentTimeMillis();if (curTime - oldTime > 100) {return true;}}return false;}
}

提交代码,成功了,但是时间太慢了,我们试着把时间差100减少一半

当把时间差改成50时,发现也是可以AC的

继续改成25,仍然可以AC

继续缩小一半,12

二分缩小,试一下6

算了,直接改成1吧(curTime - oldTime > 1),还是可以AC,但是这个时间确实慢了点
代码的时间差是大于1,也就是1ms,之所以提交会变成17ms是因为提供了17个测试用例,每个用例耗时1ms

后面就没有必要继续了,我们换一种思路吧。

思路2

由于环形链表中每个节点都有属于自己的地址,当我们遍历链表时,若出现了重复地址的情况,那么可以说明存在环形链表。
检测是否重复的方法也很简单,直接使用Set(Set一种无顺序、不可重复的数据结构),每次遍历一个节点,判断其地址是否在set中存在,若存在则说明是环形链表,否则将其存储到set中。

代码

public class Solution {public boolean hasCycle(ListNode head) {// 使用set存储hashcodeSet<Integer> set = new HashSet<>();while (head != null) {if (set.contains(head.hashCode())) return true;else set.add(head.hashCode());head = head.next;}return false;}
}

代码讲解
在存储地址之前需要检查是否存在于set,只有不存在时才可以添加到set中;
整个循环遍历之后,直接返回false,这是因为若存在环形链表的话,肯定会满足if条件,直接就返回true,反过来也就是不存在环形链表,循环结束,返回false。
提交代码,时间稍稍好一些,但是空间就不行了,由于借助了额外的空间,复杂度为O(N),接下来我们尽可能将其空间减小为O(1)。

思路3

这个方法我没有想到,我也是通过资料查阅得来的,思路3有点类似龟兔赛跑,假设兔子的速度为2,乌龟的速度为1,当然兔子没有睡觉,如下图所示:

(图案不是重点)
他们两个的跑步轨迹是这样的,兔子跑到2的时候,乌龟爬到1;兔子跑到4的时候,乌龟爬到2…兔子跑到10的时候,乌龟爬到5,如下图所示:





但如果赛道换了,变成下面这个样子:

这时,只要兔子和乌龟仍在跑,就一定会相遇!

如上图,兔子可能在跑了n圈小环的时候,乌龟刚好也进入了小环,这时候他们两个相遇了。
我们把一开始的线段赛道看成普通的链表,把带有小环的赛道看成环形链表,判断环形链表是否存在的条件就变成了判断乌龟和兔子是否相遇,而乌龟我们可以用一个慢指针表示、兔子用一个快指针表示,看代码吧:

代码

public class Solution {public boolean hasCycle(ListNode head) {// 快慢指针ListNode rabbit = head;ListNode tortoise = head;while (tortoise != null && rabbit != null && rabbit.next != null) {rabbit = rabbit.next.next;tortoise = tortoise.next;if (rabbit == tortoise) return true;}return false;}
}

代码解释
一开始的兔子变量跟乌龟变量都指向head,即都在起点开始跑

ListNode rabbit = head;
ListNode tortoise = head;

由于兔子的速度是2,即每次循环时,兔子 = 兔子的下一个的下一个;乌龟的速度为1,即每次循环时,乌龟 = 乌龟的下一个

rabbit = rabbit.next.next;
tortoise = tortoise.next;

由于每次兔子每次要走2步,因此while中除了要让当前兔子的节点不为空,还要让当前兔子的下一个不为空,这是为了避免空指针的发生。

while (tortoise != null && rabbit != null && rabbit.next != null)

当两者相遇时,即rabbit == tortoise,则返回true表示存在环形链表,提交代码,完美!

或许有人有疑问,兔子的速度一定要为2吗?其实不一定的,兔子为2乌龟为1只是为了更快的相遇,当然你也可以改成3、4、5,只不过while中的循环条件也要跟着改,略微麻烦点。


总结

很多人做完算法题之后,下次再拿出来就不会做了,我也遇到这个问题,后来我发现如果一道算法题你掌握了多种解法,并且偶尔回过头来复习,就不会那么容易忘了,这也是我为什么写此类博客的原因:在巩固自己记忆的同时, 又能帮助其他人,何乐而不为呢?

【LeetCode】详解环形链表141. Linked List Cycle Given a linked list, determine if it has a cycle in it. To相关推荐

  1. 不带头节点的链表有哪些缺点_23张图!万字详解「链表」,从小白到大佬!

    链表和数组是数据类型中两个重要又常用的基础数据类型. 数组是连续存储在内存中的数据结构,因此它的优势是可以通过下标迅速的找到元素的位置,而它的缺点则是在插入和删除元素时会导致大量元素的被迫移动,为了解 ...

  2. LeetCode实战:环形链表 II

    背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 Given a lin ...

  3. 线性表详解(静态链表、单链表、双向链表、循环链表)

    目录 申明 1. 线性表的定义 2. 线性表的抽象数据类型 3. 线性表的顺序存储结构 3. 1 顺序存储定义 3. 2 顺序存储方式 3. 3 数据长度与线性表长度区别 3. 4 地址计算方法 4. ...

  4. 刻意练习:LeetCode实战 -- Task09. 环形链表

    背景 本篇图文是LSGO软件技术团队组织的 第二期基础算法(Leetcode)刻意练习训练营 的打卡任务.本期训练营采用分类别练习的模式,即选择了五个知识点(数组.链表.字符串.树.贪心算法),每个知 ...

  5. LeetCode实战:环形链表

    题目英文 Given a linked list, determine if it has a cycle in it. To represent a cycle in the given linke ...

  6. leetcode算法题--环形链表 II★

    原题链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/ 1.map ListNode *detectCycle(ListNode *he ...

  7. 详解单链表经典OJ题

    文章目录 前言 一 删除链表中等于给定值"val"的所有节点 二 反转一个单链表 三 求中间节点 四 链表倒数第K个节点 五 合并有序链表 六 链表分割 注意细节: 七 删除重复结 ...

  8. 【LeetCode - 141142】环形链表(i和ii)(快慢指针,链表)

    https://leetcode-cn.com/problems/linked-list-cycle/ 给定一个链表,判断链表中是否有环. 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到 ...

  9. 20190829:(leetcode习题)环形链表

    环形链表 题目 大致思路 代码实现 题目 大致思路 快慢指针法:fast指针比slow指针快一步,存在环时fast可以追上slow,最后指向相同,即可判断存在环. 递归法:每次都把指针指向自己,如果有 ...

最新文章

  1. Mac OS X下Maven的安装与配置
  2. HTML锚点控制,跳转页面后定位到相应位置
  3. 老男孩Linux运维第41期20171016第六周学习重点课堂记录
  4. SpringBoot中整合Quartz
  5. 牛津临床和实验室调查手册 Oxford Handbook of Clinical and Laboratory Investigation
  6. Linux系统入门学习:在Debian或Ubuntu上安装完整的内核源码
  7. 处女座与cf(思维题)
  8. PowerDesigner15对SQLSERVER2008S数据库进行反向工程出错
  9. 具有Spring Boot和数据功能的Java头优先弹性搜索
  10. Codeforces686C【dfs】
  11. 鸿蒙系统天气主题,墨迹天气携手鸿蒙系统 以精细化气象服务助力全场景生态建设...
  12. java查询mongodb 嵌套,查询嵌套文件mongoDB
  13. [渝粤教育] 天津科技大学 人工智能导论 参考 资料
  14. 一个在线工具箱:栅栏密码加密解密工具
  15. 交通强国试点方案密集落地!智慧交通成为关键词
  16. 腾讯云服务器怎么进行学生认证?需要注意什么?
  17. 电容器在电路中的作用(很全)
  18. python+vue+elementui健身房网站管理系统django-pycharm项目
  19. Java程序员面试笔试宝典-数据库原理(三)
  20. java调用微软语音库,微软语音识别SDK总结

热门文章

  1. 贝尔链CEO-Vincent应邀出席GBLS峰会:深度解读区块链+游戏的行业现状
  2. 大数据的4v特征、数据预处理
  3. spark 之 spark是什么?
  4. 令人绝望的TensorFlow-GPU,多种报错!!!
  5. 2066: 计算鸡的蛋II
  6. SDWAN优化金融行业网络架构
  7. 生物化学《第四章蛋白质》
  8. linux ps与top 命令下wa,hi,si,st等及 VSZ,RSS,VIRT,RES,等关键字含义详解!
  9. ROS机器人程序设计学习笔记(Chapter2)
  10. 计算机软件著作权登记分类号如何选择?