环形链表
- https://leetcode.cn/problems/linked-list-cycle/
描述
-
给你一个链表的头节点 head ,判断链表中是否有环。
-
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
-
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
提示
- 链表中节点的数目范围是 [0, 10410^4104]
- -10510^5105 <= Node.val <= 10510^5105
- pos 为 -1 或者链表中的一个 有效索引
Typescript 版算法实现
1 )方案 1
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
function hasCycle(head: ListNode | null): boolean {
let p1 = head; // 单位1的速度指针
let p2 = head; // 单位2的速度指针
// while循环如果是环,则条件会一直为真,最终一定会相逢,即走到if中
// while循环中如果走到了尽头,则会跳过while, 直接 return false
while(p1 && p2?.next) {
p1 = p1.next;
p2 = p2.next.next;
// 指针重逢意味着相逢,相逢意味着闭环
if(p1 === p2) {
return true;
}
}
return false;
};
- 这里需要注意的是,题目示例中给定 pos, 但并不是作为算法的输入参数
- 这一点是比较迷惑的一点,如果示例中没有相关参数,则不能参与运算
- 解题思路
- 两个人在圆形操场上的起点同时起跑,速度快的人一定会超过速度慢的人一圈或多圈
- 用一快一慢两个指针遍历链表,如果指针能够相逢,那么链表就有环
- 慢指针,一次走一步;快指针,每次走两步
- 解题步骤
- 用一快一慢两个指针遍历链表,如果指针能够相逢,就返回true
- 遍历结束后,还没有相逢就返回false
2 )方案2
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
function hasCycle(head: ListNode | null): boolean {
// 慢指针
let slow = head
// 快指针
let fast = head?.next
while (true) {
// 边界判断
if (!fast || !fast.next) {
return false
}
// 快慢走到一起,或者快的变成慢的前驱
if (fast === slow || fast.next === slow) {
return true
}
// 正常走
slow = slow.next
fast = fast.next.next
}};
- 方案1的形式可以更快一些,因为快指针比慢指针多走一步, 设计的时候就是一步之差
- 也就是说如果有环的话,那么快指针可能会变成慢指针的前驱,这样可以提前结束
- 方案2是方案1写法上的优化, 增加了一些情况判断,减少了一些循环操作
3 )方案 3
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
function hasCycle(head: ListNode | null): boolean {
while (head?.next) {
const node = head.next // 临时存储当前节点的下一个节点
if (node === head) return true // 当前节点的下一个节点是自己,检测到闭环
head.next = head // 将当前节点赋值给当前节点的下一个节点
head = node // 将当前节点的下一个节点赋值给当前节点
}
return false
};
- 上述是官方示例,思路是基于一个点的移动,来判断是否可以找到自己
- 找到自己,则闭环,找不到或者没有后继,则不是闭环
4 )方案 4
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
function hasCycle(head: ListNode | null): boolean {
let cursor = head // 初始化游标,赋值为头部节点
const map = new Map() // 初始化Map存储结构
while (cursor) {
if (map.has(cursor)) return true // 检测到游标已经存在,则闭环
map.set(cursor, true) // 存储更新游标
cursor = cursor.next // 移动游标
}
return false
};
- 以上也是官方提供的示例程序
- 利用map存储的唯一性,来检测是否是闭环
- 这个方法效率非常好
Python3 版算法实现
1 ) 方案1
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if not head or not head.next:
return False
p1 = head # 单位1的速度指针
p2 = head # 单位2的速度指针
# while循环如果是环,则条件会一直为真,最终一定会相逢,即走到if中
# while循环中如果走到了尽头,则会跳过while, 直接 return False
while p1 and p2: # 先检查 p2 是否为 None
p1 = p1.next
if p2.next: # 确保 p2 不是 None 后才访问 p2.next
p2 = p2.next.next
else:
break # 如果 p2 是 None,则跳出循环
# 指针重逢意味着闭环
if p1 == p2:
return True
return False
2 ) 方案2
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
# 慢指针
slow = head
# 快指针
fast = head.next if head else None
while fast and fast.next:
# 边界判断
if fast == slow or fast.next == slow:
return True
# 正常走
slow = slow.next
fast = fast.next.next
# 如果没有相遇,则不存在环
return False
3 ) 方案3
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if not head or not head.next:
return False
while head.next:
node = head.next # 临时存储当前节点的下一个节点
if node == head: # 当前节点的下一个节点是自己,检测到闭环
return True
head.next = head # 将当前节点的下一个节点指向自己
head = node # 移动到下一个节点
return False
4 ) 方案4
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
cursor = head # 初始化游标,赋值为头部节点
node_map = set() # 使用集合替代 Map,因为只需要检查元素是否存在
while cursor:
if cursor in node_map:
return True # 检测到游标已经存在,则闭环
node_map.add(cursor) # 存储更新游标
cursor = cursor.next # 移动游标
return False
Golang 版算法实现
1 ) 方案1
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
p1 := head
p2 := head
for p1 != nil && p2 != nil && p2.Next != nil {
p1 = p1.Next
p2 = p2.Next.Next
if p1 == p2 {
return true
}
}
return false
}
2 ) 方案2
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
// 慢指针
slow := head
// 快指针
fast := head
// 初始时快指针先走一步
if fast != nil {
fast = fast.Next
}
// 当快指针为空或快指针的下一个节点为空时,说明链表没有环
for fast != nil && fast.Next != nil {
// 快慢指针相遇,说明有环
if slow == fast || slow == fast.Next {
return true
}
// 快慢指针各自移动
slow = slow.Next
fast = fast.Next.Next
}
// 遍历完链表都没有相遇,说明没有环
return false
}
3 ) 方案3
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
for head != nil && head.Next != nil {
node := head.Next // 临时存储当前节点的下一个节点
if node == head {
return true // 当前节点的下一个节点是自己,检测到闭环
}
head.Next = head // 将当前节点赋值给当前节点的下一个节点
head = node // 将当前节点的下一个节点赋值给当前节点
}
return false
}
4 ) 方案4
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
cursor := head // 初始化游标,赋值为头部节点
nodeMap := make(map[*ListNode]bool) // 初始化map存储结构
for cursor != nil {
if _, exists := nodeMap[cursor]; exists {
return true // 检测到游标已经存在,则闭环
}
nodeMap[cursor] = true // 存储更新游标
cursor = cursor.Next // 移动游标
}
return false
}
1259



被折叠的 条评论
为什么被折叠?



