100 lines
2.6 KiB
Go
100 lines
2.6 KiB
Go
package ds
|
|
|
|
// ds-list.go
|
|
// 泛型单链表。带哨兵头节点(dummy head),便于在 O(1) 删除首节点时统一处理边界。
|
|
// T 约束为 comparable,Delete 的值比较直接用 ==,类型安全且无需 reflect。
|
|
// 所有节点指针操作都不依赖切片下标,删除/反转只改指针,不搬移数据。
|
|
|
|
// ListNode 单链表节点。
|
|
type ListNode[T any] struct {
|
|
Val T
|
|
Next *ListNode[T]
|
|
}
|
|
|
|
// LinkedList 单链表,持有哨兵头节点和尾指针。
|
|
// head 是哨兵,不存真实数据;真实首节点是 head.Next。
|
|
// 尾指针让 PushBack 为 O(1)。
|
|
type LinkedList[T comparable] struct {
|
|
head *ListNode[T] // 哨兵头节点
|
|
tail *ListNode[T] // 指向最后一个真实节点;空表时 tail == head
|
|
}
|
|
|
|
// NewLinkedList 创建空链表。
|
|
func NewLinkedList[T comparable]() *LinkedList[T] {
|
|
dummy := &ListNode[T]{}
|
|
return &LinkedList[T]{head: dummy, tail: dummy}
|
|
}
|
|
|
|
// Len 链表长度,O(n)。链表无 size 字段时只能遍历;如需 O(1) 长度可自行加计数器。
|
|
func (l *LinkedList[T]) Len() int {
|
|
n := 0
|
|
for cur := l.head.Next; cur != nil; cur = cur.Next {
|
|
n++
|
|
}
|
|
return n
|
|
}
|
|
|
|
// Empty 是否为空。
|
|
func (l *LinkedList[T]) Empty() bool { return l.head.Next == nil }
|
|
|
|
// PushFront 头插,O(1)。
|
|
func (l *LinkedList[T]) PushFront(v T) {
|
|
node := &ListNode[T]{Val: v, Next: l.head.Next}
|
|
l.head.Next = node
|
|
if l.tail == l.head { // 之前是空表,更新尾指针
|
|
l.tail = node
|
|
}
|
|
}
|
|
|
|
// PushBack 尾插,O(1)。
|
|
func (l *LinkedList[T]) PushBack(v T) {
|
|
node := &ListNode[T]{Val: v}
|
|
l.tail.Next = node
|
|
l.tail = node
|
|
}
|
|
|
|
// Delete 删除第一个值等于 v 的节点,O(n)。返回是否删除成功。
|
|
func (l *LinkedList[T]) Delete(v T) bool {
|
|
prev := l.head
|
|
for cur := l.head.Next; cur != nil; prev, cur = cur, cur.Next {
|
|
if cur.Val == v {
|
|
prev.Next = cur.Next
|
|
if cur == l.tail { // 删的是尾节点,更新尾指针
|
|
l.tail = prev
|
|
}
|
|
cur.Next = nil // 断开引用
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Reverse 整表反转,O(n)。
|
|
func (l *LinkedList[T]) Reverse() {
|
|
var prev *ListNode[T]
|
|
cur := l.head.Next
|
|
// 反转后原来的首节点变成尾节点
|
|
if cur != nil {
|
|
l.tail = cur
|
|
}
|
|
for cur != nil {
|
|
next := cur.Next
|
|
cur.Next = prev
|
|
prev = cur
|
|
cur = next
|
|
}
|
|
l.head.Next = prev
|
|
}
|
|
|
|
// ToSlice 顺序转为切片,便于测试和遍历。
|
|
func (l *LinkedList[T]) ToSlice() []T {
|
|
var out []T
|
|
for cur := l.head.Next; cur != nil; cur = cur.Next {
|
|
out = append(out, cur.Val)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Head 返回第一个真实节点(非哨兵),空表返回 nil。供需要直接操作节点的场景使用。
|
|
func (l *LinkedList[T]) Head() *ListNode[T] { return l.head.Next }
|