这是我参与2022首次更文挑战的第38天,活动详情查看:2022首次更文挑战
题目描述
给你一个链表的head节点,然后你需要判断这个链表是否有环。
怎样代表这个链表有环?
就是这个链表中有节点的next又指向了这个链表的之前一个节点,导致链表闭环了,这样就说这个链表有环。
如果有环就返回true
, 否则就返回false
。
画个图说明下:
这个就有环的链表(闭环了,节点3又转到节点2了)
这个链表就没有环
思路分析
第一种方法
我们可以对链表进行遍历(链表没有现成的遍历方法,采用递归遍历),遍历一次就当前节点push到数组中。
另外每次push前都要先判断数组中是否存在该节点,如果有该节点,则代表着有环,返回true
即可。
如果遍历的过程中,发现当前节点为null
的时候,则代表链表没有环,已经到末尾了。返回false
即可。
代码如下:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function (head) {
const arr = []
function check (list) {
if (!list) return false
while (list) {
if (arr.includes(list)) return true
arr.push(list)
return check(list.next)
}
}
return check(head)
};
复制代码
这样写,通过是通过了,但是耗时有点久。
优化版
怎么优化呢?
因为这个是通过数组来判断是否存在的,如果数据一多,导致耗时就会变长。
我们可以对数组做个优化,使用map
来存储。节点
为key,true
为value。
通过map来寻找节点,相比数据会更快。
代码如下:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function (head) {
const map = new Map()
function check (list) {
if (!list) return false
while (list) {
if (map.get(list)) return true
map.set(list, true)
return check(list.next)
}
}
return check(head)
};
复制代码
相比数组,map
带来的提升是显而易见的。
再再优化
我们可以使用双指针法再次优化。
一个快指针指向当前节点的next节点,一个慢指针指向当前节点。
while循环遍历,快指针一次移动2步,慢指针一次移动1步,如果发现快指针和慢指针相等了,则代表着快指针跑多了一圈,代表有环,不然无法相等。
如果发现快指针为null
或者快指针的next节点为null
时,则代表没有环,返回false
即可。
代码如下:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function (head) {
if (!head) return false
let slow = head
let fast = head.next
while (slow !== fast) {
if (!fast || !fast.next) {
return false
}
slow = slow.next
fast = fast.next.next
}
return true
};
复制代码