算法_剖析环形链表之判断链表是否有环(I)

302 阅读3分钟

背景

最近有在学习算法,因此记录一波,先从链表开始吧。

环形链表(I)

题目描述

题目是力扣里面的第141题,题目的大概意思就是,给一个链表,判断此链表是否有环。
简单粗暴的意思就是,这个链表它是循环的,里面有一个圈,永远都走不完。

image.png

分析一波

思路

判断一个链表是否有环,可利用快慢指针。

笨办法

笨的方法就是遍历,这种链表是否有环跟判断数组里是否有重复数据差不多; 前端可用遍历,先定义一个空的对象,把数组遍历一遍,每遍历到一项,先判断对象里面该项值(也就是对象的属性值)在对象里面的值(属性对应的值)是不是true,是true的话,该数组就有重复的数据,直接return,否则,把该项值作为对象的属性值,并赋值为false.继续循环遍历。
但是暴力遍历的方式不符合O(1), O(1)就是不占用多余的新的内存,遍历方式,要创建一个空的对象,并且往里面新增值,就是在增加内存空间。

快慢指针

快慢指针思路(节约了空间而非时间):
指定一个慢指针p,再指定一个快指针q, p,q都指向头节点head, p每次走一步,p=p->next, q每次走两步,q=q->next->next,然后看快慢指针p q是否能相遇,能相遇就是有环,没相遇就是没有环。能走到null证明链表走完了。

上代码分析

var hasCycle = function(head){ 
if(!head) return false; 
let p=head,q=head;
while(q&&q.next){ 
p=p.next; 
q=q.next.next; 
if(p===q){ 
return true 
} 
} 
return false; 
};

先判断头指针head是否有值,没有就直接返回false,然后退出;
定义两个指针p和q分别指向头节点;这里分别创建两个指针指向头节点而不是直接使用头节点head是因为,这样的话head头节点会丢失;
然后使用循环让q和p节点一直往后走,p走一步,q走两步;
在这期间,如果p等于q了,也就是两个相遇了,就返回true; 如果在这期间直到链表走完pq一直都没有相等过,那么pq就没有相遇过,自然就没有环了,因此返回false;

总结

**这里就是用了快慢指针,定义两个快慢指针分别指向头节点head,一个走的快一点,一个走慢一点,在链表走完之前,两个指针相等了,就证明两者相遇了,也就证明有环。 ** 注意:
我上面代码里是让一个指针走1步,一个走两步,这个走几步不是死的,你可以一个走1步,2步...,另一个走3步5步都行,只要一个快一个慢就行。
就像一个圆形操场跑步比赛,一个快一个慢,一直跑总会相遇的。