一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
leetcode 142. 给定一个链表的头节点
head,返回链表开始入环的第一个节点。 如果链表无环,则返回null。
快慢指针
通过快慢指针可以判断当前链表是否有环,但是相遇位置在环的位置要怎们确定?
假设通过上述位置已经确定有环,如下图
起始位置为A,入环位置为B,快慢指针在环中相遇的位置为C,假设AB之间的距离为a,BC之间的距离为b,CB之间的距离为c,环的总长度为b + c。现在已有条件A已知为链表的头节点,C的位置也可以通过快慢指针确定,现在就是要确定B的位置或者说AB(距离为a)与 CB(c) 之间有什么关系?
- 快慢指针在
C相遇,根据快指针的速度为慢指针的2倍可以得出:慢指针走过的距离 (a + b) * 2 = 快指针走的距离(a + N * (b + c) + b)N为快指针在相遇之前在环内绕行的圈数,用公式表示:
假设环的总距离b + c 为 L,则b可以表示为 L - c,所以上等式可以改写为
则可以得出
根据上面的公式,可以看出,a的距离等于c的距离加上N - 1圈环的距离,那么如果一个指针pointA从A出发,一个指针pointC从C出发,同速前行,那么pointC绕行N - 1圈之后肯定会在B点与pointA相遇。
function detectCycle(head: ListNode | null): ListNode | null {
if (head === null) return null
// 这里快慢指针一定要从同时从head出发
let slow: ListNode | null = head, fast: ListNode | null = head
// 如果fast和slow相同,或者fast不存在退出循环
while(slow && fast) {
slow = slow.next
fast = fast.next
if (fast) {
fast = fast.next
}
if (slow === fast) {
break
}
}
// fast如果为null,表明提前到达尾结点,表明没有环
if (fast === null) {
return null
}
// 另起一个指针从头节点出发
let cur = head
while(cur && slow && cur !== slow) {
cur = cur.next
slow = slow.next
}
// 在入环点相遇,直接返回
return cur
};
哈希表
从头节点开始将所有节点依次放入哈希表中,在哈希表中再次找到相同节点,表明当前有环,且入环节点肯定是第一个已存在的节点。
function detectCycle(head: ListNode | null): ListNode | null {
let cur = head
const set = new Set<ListNode>()
while(cur) {
if (set.has(cur)) {
return cur
}
set.add(cur)
cur = cur.next
}
return null
};