本文最重要的不是如何解决这个有环问题
或许,读者觉得此文为了解决有环的问题,但此文的目的在于升华思维如何产生的?如何快速解决问题.
1.链表及方法定义
type ListNode struct {
Val int
Next *ListNode
}
func hasCycle(head *ListNode) bool {
}
(通常看到的链表)
2.常规思路分析
有环是个啥特征呢?没有环是一个啥特征呢? 如何解决一个问题的关键是明白问题是什么?如何描述言简意赅地描述清楚问题,那么解决问题就进入了关键的一步了.
最简单的判断思路:遍历所有节点,看看每个节点是否之前出现过(map),时间复杂度O(N),空间复杂度我们近似看做O(N)
3.传说中快慢指针的思路
思路的设想:不借助其他额外的存储空间,能否判断呢? 思路的精华在于:想到这思路的人,是怎么突破出这么创造性的思维的?(这有点无解,天赐灵感?),我们通常把我们自己代入问题中进行突破,所以产生了发现节点重复的问题.但这个结题的思路的提出者的,放入了两颗棋子(快慢指针),然后观察这两颗棋子在这种局面下产生的特性. 我们不知道谁提出的这个思路,但此人异于常人:他告诉我们,把自己代入问题是常人的思路.把棋子放入棋盘,让自己成为观察者,才是高人.
思路根源:只要在这棋局(有环的情况下)中,里面的棋子无论多厉害,他们都会在原地打转,蜗牛和兔子总会不断重合.
原理:
快慢指针,一前一后;
慢走一步,快走两步;
如果有环,快可追上;
如果无环,快到尽头
如果这两个指针跑到了nil上,表示到了尽头,
如果有环不会到尽头,且两个必然会重合
func hasCycle(head *ListNode) bool {
//空指针,或者就一个节点,那么必然无环
if head== nil || head.Next== nil {
return false
}
//快慢指针,一前一后
slow,fast:=head,head.Next //不用都在同一起跑线,因为快的在前
//只要不重合,那么就一直走下去
for slow!=fast{
//快指针到头了,说明没有环
if fast==nil||fast.Next==nil{
return false
}
//慢走一步
slow=slow.Next
fast=fast.Next.Next //快走两步
}
return true
}