在Go语言中手动构造带环的单链表,需要先理解单链表的基础结构,再明确环的形成逻辑,通过节点指针的指向修改即可实现带环结构的构建。

单链表基础定义
单链表由多个节点组成,每个节点包含存储数据的字段和指向下一个节点的指针,尾节点的指针通常为nil。在Go中我们可以先定义节点结构体:
// 定义单链表节点结构体
type ListNode struct {
Val int // 节点存储的值
Next *ListNode // 指向下一个节点的指针
}
构造普通单链表
首先构造一个没有环的普通单链表,依次创建节点并串联起来:
// 构造普通单链表 1->2->3->4->nil
func createNormalList() *ListNode {
// 创建各个节点
node1 := &ListNode{Val: 1}
node2 := &ListNode{Val: 2}
node3 := &ListNode{Val: 3}
node4 := &ListNode{Val: 4}
// 串联节点
node1.Next = node2
node2.Next = node3
node3.Next = node4
// node4.Next 默认为nil,作为尾节点
return node1
}
手动添加环形成带环单链表
带环单链表的核心是某个节点的Next指针不再指向后续新节点,而是指向链表中已经存在的某个节点,从而形成闭环。我们只需要在普通链表的基础上,修改尾节点的Next指向即可:
// 构造带环单链表,环的入口为节点2,结构为1->2->3->4->2...
func createCyclicList() *ListNode {
// 先创建普通链表节点
node1 := &ListNode{Val: 1}
node2 := &ListNode{Val: 2}
node3 := &ListNode{Val: 3}
node4 := &ListNode{Val: 4}
// 串联普通链表
node1.Next = node2
node2.Next = node3
node3.Next = node4
// 关键步骤:让尾节点node4的Next指向环的入口节点node2,形成环
node4.Next = node2
return node1
}
验证带环链表是否构造成功
可以通过快慢指针法检测链表是否存在环,以此验证我们的构造是否正确:
// 检测链表是否有环,有环返回true,无环返回false
func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
slow := head
fast := head.Next
for slow != fast {
// 快指针走到尾节点,说明无环
if fast == nil || fast.Next == nil {
return false
}
slow = slow.Next
fast = fast.Next.Next
}
return true
}
func main() {
normalList := createNormalList()
cyclicList := createCyclicList()
fmt.Println("普通链表是否有环:", hasCycle(normalList)) // 输出 false
fmt.Println("带环链表是否有环:", hasCycle(cyclicList)) // 输出 true
}
注意事项
- 构造环的时候,要确保指向的节点是链表中已经存在的节点,不能指向未初始化的指针,否则会出现空指针异常。
- 带环链表没有尾节点,遍历的时候如果没有环检测逻辑,会陷入无限循环,使用的时候需要特别注意。
- 环的入口节点可以根据需求修改,只需要调整尾节点Next指向的目标节点即可,比如可以让node4.Next指向node1,形成入口为节点1的环。