链表是否有环问题看似简单,但实际处理上有很多需要注意的,这个问题是非常高频笔试面试题,记忆不牢固容易遗忘,可以认真看看学习一波!有个小伙伴就在某手面试中遇到了。

判断链表是否有环

题目描述:
给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。

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

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

分析:
对于这个问题,如果没有内存空间的限制,首先想到的就是使用哈希的方法,用一个哈希存储节点,然后向下枚举链表节点:

如果发现其中有在哈希中,那么就说明有环返回true。
如果枚举到最后结束,那就说明没有环


但是这样并不满足O(1)空间复杂度的要求,我们应该怎么处理呢?

如果链表尾部有环,如果一个节点枚举到后面会在闭环中不断循环枚举,那么怎么样能高效判断有环并且能快速终止呢?

有环,其实就是第二次、第三次走过这条路才能说它有环,一个指针在不借助太多空间存储状态下无法有效判断是否有环(有可能链表很长、有可能已经在循环了),咱们可以借助 快慢指针(双指针) 啊。

其核心思想就是利用两个指针:快指针(fast)和慢指针(slow),它们两个同时从链表头遍历链表,只不过两者速度不同,如果存在环那么最终会在循环链表中相遇。

我们在具体实现的时候,可以快指针(fast)每次走两步,慢指针(slow)每次走一步。如果存在环的话快指针先进入环,慢指针后入环,在慢指针到达末尾前快指针会追上慢指针。

快慢指针如果有相遇那就说明有环,如果快指针先为null那就说明没环。

具体实现代码为:

/*** 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) {ListNode fast=head;ListNode slow=fast;while (fast!=null&&fast.next!=null) {slow=slow.next;fast=fast.next.next;if(fast==slow)return true;}return false;    }
}

提高:找到环的入口位置

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

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

这题相比上一题又难了一些,因为如果链表成环,需要找到入口。

分析:

如果不考虑内存使用,我肯定还会首先考虑哈希,将节点存着然后如果出现第二次则说明有环并直接返回,实现的代码也很简单,走投无路可以用这个方法:

/*** 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) {int pos=-1;Map<ListNode,Integer>map=new HashMap<ListNode, Integer>();ListNode team=head;while (team!=null){if(map.containsKey(team)){pos=map.get(team);return team;}else map.put(team,++pos);team=team.next;}return null;}
}

但是怎么使用O(1)的空间复杂度完成这个操作呢?上面一题的思路是使用快慢指针判断是否有环,但是怎么锁定环的入口呢?

这个题看起来是个算法题,实际上是个数学推理题。这题的关键也是快慢指针,不过需要挖掘更多的细节

回忆一下快慢指针能够挖掘的细节:

知道慢指针走了x步,快指针走了2x步,但是仅仅知道这两个条件还推导不出什么东西,我们能够进行的操作也只有用O(1)的方法进行一些操作。不过这里面快慢指针和前面有点不同的是我们前面用一个头结点开始计数。

我们还可以进行什么操作?

既然知道相遇的这个点在环内,那么我们可以用一个新的节点去枚举一圈看看环的长度是多少哇!

这里面,我们可以知道fast走的步数2x,slow走的步数x,以及环长y。

我们知道,慢指针是第一次入环,但快指针可能已经走了好几圈,但是多走的步数一定是环的整数倍(不然不可能在同一个位置相遇)。

那么可以得到 快指针步数=慢指针步数+n圈环长度。当然这里n我暂时不知道是多少。换算成公式,那就是 2x=x+ny 消去一个x得到:x=ny

上面的图我也标注快指针多走的是整数圈数。难点就在这里,需要变通:
快指针多走的x是环长y的整数倍n,慢指针走的x也是环长y的整数倍n。

那么这样有什么用呢?
如果某个节点从起点出发,走到fast,slow交汇点走的是x步(n*y步)。此时,如果某个指针从fast,slow交汇点开始如果走环长的整数倍,那么它到时候还会在原位置。

也就是说从开始head节点team1走x步,从fast,slow交汇节点team2走x步,它们最终依然到达fast,slow交汇的节点,但是在枚举的途中,一旦team1节点遍历的到环内,那么就和team2节点重合了,所以它们一旦相等那就是第一个交汇的点了。

实现代码为:

/*** 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) {boolean isloop=false;ListNode fast=new ListNode(0);//头指针ListNode slow=fast;fast.next=head;if(fast.next==null||fast.next.next==null)return null;while (fast!=null&&fast.next!=null) {fast=fast.next.next;slow=slow.next;if(fast==slow){isloop=true;break;}}if(!isloop)//如果没有环返回return null;ListNode team=new ListNode(-1);//头指针 下一个才是headteam.next=head;while (team!=fast) {team=team.next;fast=fast.next;}return team;}
}

结语

到这里,链表找环问题就解决了,代码分析可能写的不够好,有问题还请指出,再接再厉!加油!

关于作者:bigsai 主要致力于Java、数据结构与算法知识分享,有个同名原创公众号:bigsai,第一时间收获干货!

面试官问我有环链表中怎么找到入口,本以为很简单当场却想傻了相关推荐

  1. 面试官问:一个Java字符串中到底能有多少个字符?

    作者 | 鸟窝 来源 | urlify.cn/qYNR3q 依照Java的文档, Java中的字符内部是以UTF-16编码方式表示的,最小值是 \u0000 (0),最大值是\uffff(65535) ...

  2. 突然就懵了!面试官问我:线程池中多余的线程是如何回收的?

    点击关注公众号,Java干货及时送达 最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多 ...

  3. 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?

    写在前面 先说点题外话:不少读者工作几年后,仍然在使用Java7之前版本的方法,对于Java8版本的新特性,甚至是Java7的新特性几乎没有接触过.真心想对这些读者说:你真的需要了解下Java8甚至以 ...

  4. 动画:面试官问我 0.1 + 0.2 __ 0.3 ? 为什么?该如何正确回答?

    作者 | 小鹿 来源 | 小鹿动画学编程 写在前边 第一次去面试,面试官问我0.1 + 0,2 __ 0.3?估计很多人都知道在 JS 中0.1 + 0.2 != 0.3 的,至于大于还是小于还真没弄 ...

  5. 面试官问你是true还是false你可以最后反问他这个

    我们常常看到一些用==号判断是true还是false的面试题,今天就列出来几个,看看到底是true还是false,原因是什么. String s1 = "abc"; String ...

  6. 【169期】面试官问:说说为什么要限流,有哪些解决方案?

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜留言必回,有问必答! 每天 08:35 更新文章,每天进步一点点... ...

  7. 计算机往届生考研失败找工作,考研考了四年,一直没考上.现在找工作,面试官问起过去几年在干什么,该怎么回答啊?...

    考研考了四年,一直没考上.现在找工作,面试官问起过去几年在干什么,该怎么回答啊?以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一 ...

  8. 后处理程序文件大小的变量_【每日一题】(17题)面试官问:JS中事件流,事件处理程序,事件对象的理解?...

    关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 2020,实「鼠」不易 2021,「牛」转乾坤 风劲潮涌当扬帆,任重道远须奋蹄! 一.前言 2020.12.23 立 ...

  9. .jar中没有主清单属性_面试官问:为什么SpringBoot的 jar 可以直接运行?

    点击上方蓝色字体,选择"设为星标" 优质文章,及时送达 来源 | https://urlify.cn/uQvIna SpringBoot提供了一个插件spring-boot-mav ...

最新文章

  1. .net完整的图文验证
  2. 现在也是只能谢谢随笔了,但是在以后收货的日子里会有更多的感想记下
  3. 使用DynamoDBMapper插入DynamoDB项目
  4. 【vue系列之二】详解vue-cli 2.0配置文件
  5. 单片机红绿灯电路灯有几种_新农村建设的太阳能路灯如何选择?
  6. 关于sprintf的问题
  7. linux 设备挂载ppt,.linux 挂载各种设备.pdf
  8. windows之2012缺少api-ms-win-crt**.dll
  9. 如何制造万能版的Ghost版Windows?[转]
  10. oracle宿舍管理系统实训报告,学生宿舍管理系统_开题报告
  11. python打开360浏览器_Selenium安装与360浏览器使用
  12. matlab simulink仿真实现电力电子的整流电路
  13. 百度云平台BAE空间申请
  14. 战火与秩序迁城显示服务器忙,战火与秩序迁城方法介绍
  15. SAP-MM知识精解-STO公司内的库存转储订单(01)-不带交货单配置及操作
  16. 聊聊soho办公-人们为什么越来越累
  17. 树莓派(raspberry pi)日记1之个人网站的构建(localhost内网穿透实现公网可以访问)
  18. 【Ubuntu日常技巧】Ubuntu开机自动设置笔记本屏幕亮度
  19. 黑域神器(解决卡顿)详细安装启动步骤
  20. 算法设计与分析之平摊分析

热门文章

  1. C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板和友元
  2. 购物小票 FoundPrice.java
  3. 【Flask项目】项目准备之-创建User模型类
  4. Android Makefile编译流程
  5. IPFS(星际文件系统)的安装与使用
  6. SSL/TSL双向认证过程与Wireshark抓包分析
  7. KTHREAD 结构体属性介绍
  8. 2020-10-30(smali复杂类解析)
  9. 160个Crackme008
  10. android 开发文档模板