1、描述
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从0开始)。如果 pos 是 -1, 则在该链表中没有环。
例1:输入:head = [3, 2, 0, -4] pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个结点
例2:输入:head = [1, 2] pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个结点
2、算法:
链表结点:
public class ListNode{
public var val : Int
public var next : ListNode?
public init(_ val : Int){
self.val = val
self.next = nil
}
}
1)哈希表
思想:可以通过检查一个结点此前是否被访问过来判断链表是否为环形链表
我们遍历所有结点并在哈希表中存储每个结点的引用(或内存地址)。如果当前结点为空结点 nil即以检测到链表尾部的下一个结点,那么我们已经遍历完整个链表,并且该链表不是环形链表。如果当前结点的引用已经存在于哈希表中,那么返回true即该链表为环形链表
时间复杂度:O(n),对于含有n 个元素的链表,我们访问每个元素最多一次。添加一个结点到哈希表中只需要花费O(1) 的时间。
func hasCycle(_ head:ListNode?)->Bool{
/*
哈希表
*/
var head = head
var nodeSeen : [Int:ListNode?] = [:]
while head != nil {
if nodeSeen[(head?.val)!] != nil {
return true
}else{
nodeSeen[(head?.val)!] = head
}
head = head?.next
}
return false
}
2)双指针
思路:双指针
我们遍历所有结点并在哈希表中存储每个结点的引用(或内存地址)。如果当前结点为空结点 nil即以检测到链表尾部的下一个结点,那么我们已经遍历完整个链表,并且该链表不是环形链表。如果当前结点的引用已经存在于哈希表中,那么返回true即该链表为环形链表
时间复杂度:O(n),让我们将 n 设为链表中结点的总数。
func hasCycle2(_ head:ListNode?)->Bool{
/*
双指针
*/
if head == nil || head?.next == nil {
return false
}
var slow = head
var fast = head?.next
while slow?.val != fast?.val {
if fast == nil || fast?.next == nil{
return false
}
slow = slow?.next
fast = fast?.next?.next
}
return true
}