在 Go 语言中,单链表由一系列节点组成,每个节点包含数据域和指向下一个节点的指针。带环的单链表是指链表中的某个节点通过其 next 指针指向了链表中更早出现的节点,从而形成闭环。手动构建这样的结构,通常用于验证环检测算法的正确性。

定义链表节点
首先我们需要定义一个简单的节点结构体,用来保存整数值和下一个节点的地址。
package main
import "fmt"
// ListNode 表示单链表节点
type ListNode struct {
Val int
Next *ListNode
}
构建普通单链表
我们可以先创建几个节点,并把它们按顺序连起来,形成一个没有环的链表。
func buildNormalList() *ListNode {
n1 := &ListNode{Val: 1}
n2 := &ListNode{Val: 2}
n3 := &ListNode{Val: 3}
n4 := &ListNode{Val: 4}
n1.Next = n2
n2.Next = n3
n3.Next = n4
return n1
}
手动制造环
要在上面的链表中制造环,只需要把尾节点 n4 的 Next 指向之前的某个节点,例如 n2。这样从 n1 出发遍历,会经过 n2、n3、n4,然后又回到 n2。
func buildCyclicList() *ListNode {
head := buildNormalList()
// 找到尾节点 n4 和环入口 n2
n2 := head.Next
n4 := n2.Next.Next
// 手动让尾节点指向 n2,形成环
n4.Next = n2
return head
}
验证环的存在
我们可以用 Floyd 判圈算法来确认链表确实带环。该算法使用快慢指针,若二者相遇则说明有环。
func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
slow := head
fast := head.Next
for fast != nil && fast.Next != nil {
if slow == fast {
return true
}
slow = slow.Next
fast = fast.Next.Next
}
return false
}
func main() {
cyclic := buildCyclicList()
fmt.Println("链表是否带环:", hasCycle(cyclic))
}
注意事项
- 在构建环时,要确保被指向的节点已经在链表中,否则会形成独立环或丢失原有结构。
- 带环链表不能简单地用遍历到 nil 的方式释放,需要额外记录访问过的节点。
- 在单元测试中构造带环链表,能有效检验检测逻辑的边界情况。
通过上述方式,我们便可以在 Go 中手动构建带环的单链表,并为后续的算法练习提供可控的测试数据。