首先要知道链表是怎么定义的:
第一种:
//定义一个结构体来表示一个链表中的一个节点
type ListNode struct {
Val int
Next *ListNode
}
//其中,Val是一个整数,用于存储节点的值;Next是一个指向下一个节点的指针。
//使用Next指针,可以将不同的节点链接在一起,形成一个链表。
node1 := &ListNode{Val: 1}
node2 := &ListNode{Val: 2}
node3 := &ListNode{Val: 3}
node1.Next = node2
node2.Next = node3
第二种:
如果要定义多个链表,可以使用有意义的变量名来区分不同的链表
package main
import "fmt"
type ListNode struct {
Val int
Next *ListNode
}
func main() {
// 定义两个链表
var listA, listB *ListNode
// 操作链表 A
listA = &ListNode{Val: 1}
listA.Next = &ListNode{Val: 2}
listA.Next.Next = &ListNode{Val: 3}
// 操作链表 B
listB = &ListNode{Val: 4}
listB.Next = &ListNode{Val: 5}
listB.Next.Next.Next = &ListNode{Val: 7}
// 打印链表 A
cur := listA
for cur != nil {
fmt.Printf("%d -> ", cur.Val)
cur = cur.Next
}
fmt.Println("nil")
// 打印链表 B
cur = listB
for cur != nil {
fmt.Printf("%d -> ", cur.Val)
cur = cur.Next
}
fmt.Println("nil")
}
法一:哈希表
同一个map数组存储遍历过的结点,这个map数组只需要指向结点的指针的键即可,不需要关心结点的值是多少,也就是map对应的key的value值。所以value可以用一个空结构体代替。
遍历链表的条件:指针指向的结点不为空时head!=nil
用if _, ok:=m[head];ok{}语句来判断head指针指向的结点是否出现在m[head]里面,ok是一个bool类型的变量,如果存在,m[head]给ok返回一个非0的值,在这里返回的是一个空结构体struct,此时ok的值为true;如果不存在,m[head]给ok返回一个0值。
如果不存在,就把节点存到/标记到map数组里,m[head]=struct{}{}。只用标记键head就可以,不需要value值存入map数组,value同一个空结构体来代替,struct{}{}只是一种特殊的数据类型,并不占据内存空间。
然后指针移到下一节点
如果跳出for循环,则返回false。
func hasCycle(head *ListNode) bool {
m:=map[*ListNode]struct{}{}
for head!=nil{
if _,exists:=m[head];exists{
return true
}
m[head]=struct{}{}
head=head.Next
}
return false
}
时间复杂度:O(n),n为节点数
空间复杂度:O(n),n为节点数
法二:
slow指针一步一步走;
fast指针两步两步走;
如果存在环,fast一定会和slow相遇
//快慢指针
func hasCycle2(head *ListNode) bool{
if head==nil||head.Next==nil{
return false
}
slow,fast:=head,head.Next
for slow!=fast{
//这里的if语句是错的
//if slow==nil||fast==nil{
// return false
//}
//这里的代码中,每次更新慢指针和快指针时,都是将它们都更新为头结点的下一个节点,这是错误的。
//slow=head.Next
//fast=head.Next.Next
//fast指针走比slow的快,所以当fast为nil或者fast.Next为nil时,就已经能说明没有环。
if fast==nil||fast.Next==nil{
return false
}
slow=slow.Next
fast=fast.Next.Next
}
return true
}
时间复杂度:O(n),n为节点数
空间复杂度:O(1)