Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
leetcode141、142环形链表
一、环形链表及判环
当链表中有环时,循环链表是死循环,一直去next一直有,所以链表判环很重要。
那么如何判断链表有环呢:
- 最简单粗暴的进行一定量级的循环,若一直循环不结束可以认为是有环的。但是这种方法并不准确。
- 使用一个对象或数组或是set格式的缓存数据,循环链表时将每一项都存到这个缓存数据中,然后在接下来的循环中查找,如果有重复着出现在缓存数据中,表示有环。但是这样的处理时间复杂度是o(n)空间复杂度也是o(n)(对象类的数据格式)。
- 还有一种方法:定义两个快慢指针,如果链表有环,那么跑的快的指针一定会追上慢指针(两个指针指向的链表节点相同)。如果追上了,那就表示链表有环
二、题目示例
以141环形链表Ⅰ为例:
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
三种方法解题:
- 量级遍历
var hasCycle = function(head) {
// 1. 量级遍历
let count = 0
while(head) {
if (count>10000) {// 设置量级为1万
return true
}
count+=1;
head = head.next
}
return false
};
这种解法我们在leetcode中提交,居然是可以通过的!
但是很明显这种方法并不严谨
2. 定义缓存对象/数组结构来判断是否有环
var hasCycle = function(head) {
// 这里我们使用set结构,当然也可以使用其他类型的数据结构
let cache = new Set()
while(head) {
if (cache.has(head)) {
return true
} else {
cache.add(head)
}
head = head.next
}
return false
}
我们知道这种方法下空间复杂度为o(n)当链表很大时,这个缓存结构也会很大,于是我们可以用常量级0(1)的空间复杂度定义两个指针来优化这种方法⬇
3. 使用快慢两个指针进行判断
var hasCycle = function(head) {
let fast = head, slow = head; // 两个指针初始都指向头节点
while(fast&&fast.next) {
slow = slow.next; // 慢指针每次跑一步
fast = fast.next.next; // 快指针每次跑两步
if (slow===fast) { // 如果两个指针的节点相同,表示追上了,有环
return true
}
}
return false
}
三、环形链表入口判定
我们将环形链表看成数学题目来计算:
由上面的推导我们知道,快慢指针的相交点—>环的起始点的距离 = 头节点—>环的起始点的距离。对应到图示中的链表,我们即知道快慢指针的相遇点为 6节点
处(1->2->3 = 6->7->3
)
现在我们再看142环形链表Ⅱ这道题:
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
跟141题不同的是,这道题有环需要返回环的起始节点,无环则返回null。
那么我们可以用两个快慢指针去判断是否有环,当两指针相遇时,表示有环时,且根据上述公式,相遇点和头节点同时往后next查找节点,当节点相同时,这个节点即为环的起始点。
代码实现:
var detectCycle = function(head) {
const node = hasCycle(head)
if (node) { // 有相遇点
// 根据相遇点找和头节点找环的起始点
let n1 = head, n2 = node;
while(n1!==n2) {
n1 = n1.next;
n2 = n2.next;
}
return n1
}
// 无相遇点返回null
return node
};
// 可以直接将上题的判环逻辑拿来复用,但是return的结果要稍微修改下
var hasCycle = function(head) {
let fast = head, slow = head; // 快慢指针判环
while(fast&&fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow===fast) {
return slow; // 有环return指针的相遇点
}
}
return null; // 无环return null
}