携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
环形链表
描述
给出一个链表的头节点head,判断链表中是否有环。
如上图所示,这样的链表就是存在环。可以很清楚的看到。理论的解释是从头节点开始往后一直查找next节点,最终还会找到原已经经过的节点,就表示这个是环形链表。
分析
如果是空或者单节点链表,肯定就不会是环形链表,直接返回false。
如果有两个人在操场上跑步,假如速度固定,一个人快,一个人慢,他们会相遇吗?
可能会,也可能不会。这主要看跑道是怎么设计的,如果是百米跑的直线,那肯定不会,如果是千米的环形跑道,那就会相遇,速度快的同学在后面还会追上速度较慢的同学,两者相遇。
我们从这个例子就可以找到解决方案。
设置两个指针,一个指针每次走一步,另一个指针每次走两步,就相当于跑步的快慢两个同学,如果存在环,则终究会相遇,如果跑到尽头都没相遇,那则不是环形链表。
- 我们先设置前三个需要初始化的节点不能为空,如果为空,则直接返回结果不是环形链表
- 通过while不断循环,当行走的两个指针不同,则一直循环下去:有两个结果,两个指针相同了,跳出while循环,返回true,或里面出现某个节点为空返回false
程序实现
通过以上分析,具体实现如下:
var hasCycle = function(head) {
// 如果存在环,则不存在为null的情况
if (!head || !head.next || !head.next.next) return false
// 添加两个快慢指针:一个走一步,另一个走两步
let l1 = head.next
let l2 = head.next.next
while (l1 !== l2) {
if (!l1 || !l1.next || !l2 || !l2.next || !l2.next.next) return false
// 快慢指针不断往前走
l1 = l1.next
l2 = l2.next.next
}
return true
};
需要注意一个问题:刚开始我用的是hasCycle递归函数来执行,发现死循环了,对于这种本身可能是无限循环的结构,递归的条件比循环结构的条件更加难以控制,这种场景下用while循环明显更佳。