「这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战」。
题目描述
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中存在环,则返回 true
。 否则,返回 false
。
Map思路分析(哈希表思路)
此题第一种思路可以是Map方法,分析如下:
如果我们要判断链表是否有环,可以先创建一个map类型的结构
将每一个链表节点都放进去
遍历到最后
链表结束,那么这个链表就不是环形链表
链表出现重复项,此链表为环形链表
分析边界情况,如果此heade为null 或者只有一个节点,return false;
Map 代码
var hasCycle = function (head) {
if (!head || !head.next) return false;
const mapper = new Map();
let currentNode = head.next;
while (currentNode) {
if (mapper.has(currentNode)) {
return true;
} else {
mapper.set(currentNode, currentNode);
}
currentNode = currentNode.next;
}
return false;
};
复杂度分析
时间复杂度:O(N)O(N),其中 NN 是链表中的节点数。最坏情况下我们需要遍历每个节点一次。
空间复杂度:O(N)O(N),其中 NN 是链表中的节点数。主要为哈希表的开销,最坏情况下我们需要将每个节点插入到哈希表中一次。
快慢节点思路分析
快慢指针,顾名思义,首先需要两个指针,一快一慢
慢指针指向第一个变量,快指针指向下一个,每次向后移动,慢指针每次走一步,快指针每次走两步。
如果链表有环,则快慢指针一定会相遇(我理解这里是数学思维最大公约数的情况)
如果快慢指针相遇,则链表有环
如果快指针结束,则链表无环
边界值分析与Map同款。
图示分析
快慢指针代码实现
var hasCycle = function (head) {
if (!head || !head.next) return false;
let preNode = head;
let nextNode = head.next;
while (preNode !== nextNode) {
if (!nextNode || !nextNode.next) return false;
preNode = preNode.next;
nextNode = nextNode.next.next;
}
return true;
};
复杂度分析
时间复杂度:O(N)O(N),其中 NN 是链表中的节点数。
当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。
当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 NN 轮。
空间复杂度:O(1)O(1)。我们只使用了两个指针的额外空间。