算法题学习 --- 判断链表中是否有环
题目
描述
判断给定的链表中是否有环。如果有环则返回 true,否则返回 false。
数据范围:链表长度 0≤n≤10000,链表中任意节点的值满足∣val∣<=100000
要求:空间复杂度 O(1),时间复杂度 O(n)
输入分为两部分,第一部分为链表,第二部分代表是否有环,然后将组成的 head 头结点传入到函数里面。-1 代表无环,其它的数字代表有环,这些参数解释仅仅是为了方便读者自测调试。实际在编程时读入的是链表的头节点。
例如输入{3,2,0,-4},1 时,对应的链表结构如下图所示:
可以看出环的入口结点为从头结点开始的第 1 个结点(注:头结点为第 0 个结点),所以输出 true。
示例 1
示例 2
示例 3
分析
考虑特殊情况
在进行问题分析过程前,我们先考虑一下特殊情况,那么在这道问题中,有哪些情况是特殊的呢?
不难发现,如下情况:
如果输入的头结点为空,那么返回头结点即可
如果输入的头结点的 next 为空,那么也同样返回头结点即可。
考虑一般情况
很容易想到第一种方法:
我们从头节点开始遍历链表,并使用 set 记录每个节点的指针,如果没有发现相同的就 insert 到 set 中,如果发现有相同的,那就说明出现了重复的指针,这时就是出现了环。
很明显,这种方法在最坏的情况下面,会占用 O(n)的空间,并不满足题目的要求,基于此,我们实现的是版本一的代码。
第二种方法,我们来观察一下链表中有环的情况,因为链表中只有一个指针域,如果出现环,那么一定是末尾指向 NULL 的指针指向了前面的节点,换言之,有环链表中没有指针域为 NULL 的节点。
那么我们要如何才能判断我们的遍历是一直在环中循环还是因为链表太长还未遍历到末尾呢?
这时,我们就需要用到双指针了,双指针指的是在遍历对象的过程中使用两个指针,同方向不同速度(快慢指针),同方向同速度,不同方向同速度(对撞指针)来进行访问。
考虑这样一种用法,我们初始化两个指针都指向 head,然后快指针每次走两步,慢指针每次走一步,如果存在环的话,由于速度差异,那么它们一定会在某一时刻碰到对方,此时也就说明是存在环的。
代码示例
版本一
利用了 std::set,空间占用达到 O(n)
版本二
如下:
step 1:设置快慢两个指针,初始都指向链表头。
step 2:遍历链表,快指针每次走两步,慢指针每次走一步。
step 3:如果快指针到了链表末尾,说明没有环,因为它每次走两步,所以要验证连续两步是否为 NULL。
step 4:如果链表有环,那快慢双指针会在环内循环,因为快指针每次走两步,因此快指针会在环内追到慢指针,二者相遇就代表有环。
版权声明: 本文为 InfoQ 作者【桑榆】的原创文章。
原文链接:【http://xie.infoq.cn/article/a992c1412e08afdb34a9c4466】。文章转载请联系作者。
评论