This commit is contained in:
2026-07-20 19:57:25 +08:00
parent 4fa78d4c00
commit 124ad9afdf
26 changed files with 2715 additions and 5 deletions
+151
View File
@@ -0,0 +1,151 @@
package ds
// ds-btree.go
// 普通二叉树(不强制 BST 性质)+ 层序构造。
// 贴合 leetcode 题目里 [1,2,3,null,null,4,5] 这种层序数组表示。
// 复用 ds-tree.go 里定义的 TreeNode[T] 和 Ordered 约束,避免重复定义。
// BuildTree 从 leetcode 风格的层序数组构造二叉树。
// vals 中 *T 为 nil 的位置表示空节点;数组末尾的 nil 可省略。
// 示例:[1,2,3,nil,nil,4,5] →
//
// 1
// / \
// 2 3
// / \
// 4 5
func BuildTree[T Ordered](vals []*T) *TreeNode[T] {
if len(vals) == 0 || vals[0] == nil {
return nil
}
root := &TreeNode[T]{Val: *vals[0]}
q := &Queue[*TreeNode[T]]{}
q.Enqueue(root)
i := 1
for !q.Empty() && i < len(vals) {
node, _ := q.Dequeue()
// 左孩子
if i < len(vals) && vals[i] != nil {
node.Left = &TreeNode[T]{Val: *vals[i]}
q.Enqueue(node.Left)
}
i++
// 右孩子
if i < len(vals) && vals[i] != nil {
node.Right = &TreeNode[T]{Val: *vals[i]}
q.Enqueue(node.Right)
}
i++
}
return root
}
// TreeToLevelOrder 把树序列化回层序数组(与 BuildTree 互逆),nil 占位。
// 末尾连续的 nil 会被裁掉,与 leetcode 表示一致。
func TreeToLevelOrder[T any](root *TreeNode[T]) []*T {
if root == nil {
return nil
}
var out []*T
q := &Queue[*TreeNode[T]]{}
q.Enqueue(root)
for !q.Empty() {
node, _ := q.Dequeue()
if node == nil {
out = append(out, nil)
continue
}
v := node.Val
out = append(out, &v)
q.Enqueue(node.Left)
q.Enqueue(node.Right)
}
// 裁掉末尾 nil
for len(out) > 0 && out[len(out)-1] == nil {
out = out[:len(out)-1]
}
return out
}
// MaxDepth 求二叉树最大深度(空树 0,单节点 1)。对任意二叉树都适用,不限于 BST。
func MaxDepth[T any](root *TreeNode[T]) int {
if root == nil {
return 0
}
l := MaxDepth(root.Left)
r := MaxDepth(root.Right)
if l > r {
return l + 1
}
return r + 1
}
// InOrderAny 对任意二叉树做中序遍历(不限于 BST,结果未必有序)。
// 复用 TreeNode[T],不依赖 BST 类型,方便裸树题目直接调用。
func InOrderAny[T any](root *TreeNode[T]) []T {
var out []T
var walk func(*TreeNode[T])
walk = func(n *TreeNode[T]) {
if n == nil {
return
}
walk(n.Left)
out = append(out, n.Val)
walk(n.Right)
}
walk(root)
return out
}
// PreOrderAny 对任意二叉树做前序遍历。
func PreOrderAny[T any](root *TreeNode[T]) []T {
var out []T
var walk func(*TreeNode[T])
walk = func(n *TreeNode[T]) {
if n == nil {
return
}
out = append(out, n.Val)
walk(n.Left)
walk(n.Right)
}
walk(root)
return out
}
// PostOrderAny 对任意二叉树做后序遍历。
func PostOrderAny[T any](root *TreeNode[T]) []T {
var out []T
var walk func(*TreeNode[T])
walk = func(n *TreeNode[T]) {
if n == nil {
return
}
walk(n.Left)
walk(n.Right)
out = append(out, n.Val)
}
walk(root)
return out
}
// LevelOrderAny 对任意二叉树做层序遍历。裸树版,不依赖 BST。
func LevelOrderAny[T any](root *TreeNode[T]) []T {
if root == nil {
return nil
}
var out []T
q := &Queue[*TreeNode[T]]{}
q.Enqueue(root)
for !q.Empty() {
node, _ := q.Dequeue()
out = append(out, node.Val)
if node.Left != nil {
q.Enqueue(node.Left)
}
if node.Right != nil {
q.Enqueue(node.Right)
}
}
return out
}
+107
View File
@@ -0,0 +1,107 @@
package ds
import "testing"
// 辅助:int 指针构造器,简化测试用例。
func iptr(i int) *int { return &i }
func TestBuildTreeRoundTrip(t *testing.T) {
// 1
// / \
// 2 3
// / \
// 4 5
vals := []*int{iptr(1), iptr(2), iptr(3), nil, nil, iptr(4), iptr(5)}
root := BuildTree[int](vals)
if root == nil {
t.Fatal("BuildTree 不应返回 nil")
}
// 层序应为 [1,2,3,nil,nil,4,5]
out := TreeToLevelOrder[int](root)
want := []*int{iptr(1), iptr(2), iptr(3), nil, nil, iptr(4), iptr(5)}
if len(out) != len(want) {
t.Fatalf("TreeToLevelOrder 长度 = %d, want %d", len(out), len(want))
}
for i := range want {
if (out[i] == nil) != (want[i] == nil) {
t.Fatalf("位置 %d: out nil=%v, want nil=%v", i, out[i] == nil, want[i] == nil)
}
if out[i] != nil && *out[i] != *want[i] {
t.Fatalf("位置 %d: out = %d, want %d", i, *out[i], *want[i])
}
}
}
func TestBuildTreeTraversals(t *testing.T) {
// 1
// / \
// 2 3
// / \
// 4 5
vals := []*int{iptr(1), iptr(2), iptr(3), iptr(4), iptr(5)}
root := BuildTree[int](vals)
pre := PreOrderAny[int](root)
wantPre := []int{1, 2, 4, 5, 3}
for i := range wantPre {
if pre[i] != wantPre[i] {
t.Fatalf("PreOrderAny[%d] = %d, want %d", i, pre[i], wantPre[i])
}
}
in := InOrderAny[int](root)
wantIn := []int{4, 2, 5, 1, 3}
for i := range wantIn {
if in[i] != wantIn[i] {
t.Fatalf("InOrderAny[%d] = %d, want %d", i, in[i], wantIn[i])
}
}
post := PostOrderAny[int](root)
wantPost := []int{4, 5, 2, 3, 1}
for i := range wantPost {
if post[i] != wantPost[i] {
t.Fatalf("PostOrderAny[%d] = %d, want %d", i, post[i], wantPost[i])
}
}
lvl := LevelOrderAny[int](root)
wantLvl := []int{1, 2, 3, 4, 5}
for i := range wantLvl {
if lvl[i] != wantLvl[i] {
t.Fatalf("LevelOrderAny[%d] = %d, want %d", i, lvl[i], wantLvl[i])
}
}
}
func TestBuildTreeEdgeCases(t *testing.T) {
// 空数组
if r := BuildTree[int](nil); r != nil {
t.Fatal("空数组应返回 nil")
}
// 数组首元素为 nil
if r := BuildTree[int]([]*int{nil, iptr(1)}); r != nil {
t.Fatal("首元素 nil 应返回 nil")
}
// 单节点
root := BuildTree[int]([]*int{iptr(42)})
if root.Val != 42 || root.Left != nil || root.Right != nil {
t.Fatalf("单节点树结构错误: %+v", root)
}
}
func TestMaxDepth(t *testing.T) {
// 1
// / \
// 2 3
// /
// 4
vals := []*int{iptr(1), iptr(2), iptr(3), iptr(4)}
root := BuildTree[int](vals)
if d := MaxDepth[int](root); d != 3 {
t.Fatalf("MaxDepth = %d, want 3", d)
}
if d := MaxDepth[int](nil); d != 0 {
t.Fatalf("空树 MaxDepth = %d, want 0", d)
}
}
+100
View File
@@ -0,0 +1,100 @@
package ds
// ds-deque.go
// 泛型双端队列,基于环形缓冲。PushFront/PushBack/PopFront/PopBack 全 O(1) 均摊。
// 当容量不足时扩容为 2 倍,自动重新对齐。
// Deque 双端队列,零值可用。
type Deque[T any] struct {
buf []T
head int // 队首索引,逆时针(向后)增长
tail int // 队尾索引 +1
n int // 元素个数
}
// Len 元素个数。
func (d *Deque[T]) Len() int { return d.n }
// Empty 是否为空。
func (d *Deque[T]) Empty() bool { return d.n == 0 }
// PushBack 尾部插入。
func (d *Deque[T]) PushBack(v T) {
d.grow()
d.buf[d.tail] = v
d.tail = (d.tail + 1) % len(d.buf)
d.n++
}
// PushFront 头部插入。
func (d *Deque[T]) PushFront(v T) {
d.grow()
d.head = (d.head - 1 + len(d.buf)) % len(d.buf)
d.buf[d.head] = v
d.n++
}
// PopBack 尾部弹出;空返回零值和 false。
func (d *Deque[T]) PopBack() (T, bool) {
var zero T
if d.n == 0 {
return zero, false
}
d.tail = (d.tail - 1 + len(d.buf)) % len(d.buf)
v := d.buf[d.tail]
d.buf[d.tail] = zero
d.n--
return v, true
}
// PopFront 头部弹出;空返回零值和 false。
func (d *Deque[T]) PopFront() (T, bool) {
var zero T
if d.n == 0 {
return zero, false
}
v := d.buf[d.head]
d.buf[d.head] = zero
d.head = (d.head + 1) % len(d.buf)
d.n--
return v, true
}
// Front 查看头部,不移除。
func (d *Deque[T]) Front() (T, bool) {
var zero T
if d.n == 0 {
return zero, false
}
return d.buf[d.head], true
}
// Back 查看尾部,不移除。
func (d *Deque[T]) Back() (T, bool) {
var zero T
if d.n == 0 {
return zero, false
}
return d.buf[(d.tail-1+len(d.buf))%len(d.buf)], true
}
// grow 容量不足时扩容并重新对齐。
func (d *Deque[T]) grow() {
if d.buf == nil {
d.buf = make([]T, 8)
return
}
if d.n < len(d.buf) {
return
}
oldCap := len(d.buf)
newCap := oldCap * 2
nb := make([]T, newCap)
// 把 head..head+n 的环形数据复制到新缓冲区前端。
for i := 0; i < d.n; i++ {
nb[i] = d.buf[(d.head+i)%oldCap]
}
d.buf = nb
d.head = 0
d.tail = d.n
}
+78
View File
@@ -0,0 +1,78 @@
package ds
import "testing"
func TestDequeBothEnds(t *testing.T) {
var d Deque[int]
d.PushBack(2)
d.PushBack(3)
d.PushFront(1)
d.PushFront(0)
// 队列内容应为 [0,1,2,3]
if got, _ := d.Front(); got != 0 {
t.Fatalf("Front = %d, want 0", got)
}
if got, _ := d.Back(); got != 3 {
t.Fatalf("Back = %d, want 3", got)
}
if d.Len() != 4 {
t.Fatalf("Len = %d, want 4", d.Len())
}
// 交替弹出,验证两端 O(1)
if v, _ := d.PopFront(); v != 0 {
t.Fatalf("PopFront = %d, want 0", v)
}
if v, _ := d.PopBack(); v != 3 {
t.Fatalf("PopBack = %d, want 3", v)
}
if v, _ := d.PopFront(); v != 1 {
t.Fatalf("PopFront = %d, want 1", v)
}
if v, _ := d.PopBack(); v != 2 {
t.Fatalf("PopBack = %d, want 2", v)
}
if !d.Empty() {
t.Fatal("应为空")
}
}
func TestDequeGrowAcrossBoundary(t *testing.T) {
// 跨越 head 边界触发扩容,验证数据顺序不乱。
var d Deque[int]
for i := 0; i < 20; i++ {
d.PushBack(i)
}
// 先弹出几个,让 head 前移,再继续插入触发扩容。
for i := 0; i < 5; i++ {
_, _ = d.PopFront()
}
for i := 20; i < 40; i++ {
d.PushBack(i)
}
// 期望顺序 5..39
for want := 5; want < 40; want++ {
got, ok := d.PopFront()
if !ok || got != want {
t.Fatalf("PopFront = %d,%v, want %d", got, ok, want)
}
}
}
func TestDequeString(t *testing.T) {
var d Deque[string]
d.PushFront("b")
d.PushFront("a")
d.PushBack("c")
var got []string
for !d.Empty() {
v, _ := d.PopFront()
got = append(got, v)
}
want := []string{"a", "b", "c"}
for i := range want {
if got[i] != want[i] {
t.Fatalf("结果[%d] = %q, want %q", i, got[i], want[i])
}
}
}
+101
View File
@@ -0,0 +1,101 @@
package ds
// ds-heap.go
// 泛型二叉堆,手动实现 siftUp/siftDown,不依赖 container/heap。
// 通过 less 函数决定堆性质:less(a,b)=true 表示 a 应在 b 上方。
// - 传 a < b → 小顶堆
// - 传 a > b → 大顶堆
// Push/Pop 均 O(log n),Peek O(1)。
// Heap 泛型堆。零值不可直接用,必须用 NewHeap 创建。
type Heap[T any] struct {
data []T
less func(a, b T) bool
}
// NewHeap 创建堆。less(a,b) 返回 true 表示 a 优先级更高(更靠堆顶)。
func NewHeap[T any](less func(a, b T) bool) *Heap[T] {
return &Heap[T]{less: less}
}
// Len 元素个数。
func (h *Heap[T]) Len() int { return len(h.data) }
// Empty 是否为空。
func (h *Heap[T]) Empty() bool { return len(h.data) == 0 }
// Peek 查看堆顶,不移除。
func (h *Heap[T]) Peek() (T, bool) {
var zero T
if len(h.data) == 0 {
return zero, false
}
return h.data[0], true
}
// Push 插入元素:先追加到末尾,再 siftUp。
func (h *Heap[T]) Push(v T) {
h.data = append(h.data, v)
h.siftUp(len(h.data) - 1)
}
// Pop 弹出堆顶:把末尾元素换到堆顶,缩容,再 siftDown。
func (h *Heap[T]) Pop() (T, bool) {
var zero T
n := len(h.data)
if n == 0 {
return zero, false
}
top := h.data[0]
if n == 1 {
h.data = h.data[:0]
return top, true
}
h.data[0] = h.data[n-1]
h.data[n-1] = zero // 置零释放引用
h.data = h.data[:n-1]
h.siftDown(0)
return top, true
}
// Init 从已有切片原地建堆,O(n)。调用后 h 持有该切片的引用。
func (h *Heap[T]) Init(data []T) {
h.data = data
// 从最后一个非叶子节点开始向下调整。
for i := len(h.data)/2 - 1; i >= 0; i-- {
h.siftDown(i)
}
}
// siftUp 从 i 向上调整。
func (h *Heap[T]) siftUp(i int) {
for i > 0 {
parent := (i - 1) / 2
if !h.less(h.data[i], h.data[parent]) {
break
}
h.data[i], h.data[parent] = h.data[parent], h.data[i]
i = parent
}
}
// siftDown 从 i 向下调整。
func (h *Heap[T]) siftDown(i int) {
n := len(h.data)
for {
l := 2*i + 1
r := 2*i + 2
best := i
if l < n && h.less(h.data[l], h.data[best]) {
best = l
}
if r < n && h.less(h.data[r], h.data[best]) {
best = r
}
if best == i {
break
}
h.data[i], h.data[best] = h.data[best], h.data[i]
i = best
}
}
+113
View File
@@ -0,0 +1,113 @@
package ds
import (
"math/rand"
"sort"
"testing"
)
func TestHeapMinOrder(t *testing.T) {
h := NewHeap[int](func(a, b int) bool { return a < b })
for _, v := range []int{5, 3, 8, 1, 4, 7, 9, 2, 6} {
h.Push(v)
}
var got []int
for !h.Empty() {
v, _ := h.Pop()
got = append(got, v)
}
want := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for i := range want {
if got[i] != want[i] {
t.Fatalf("Pop 顺序[%d] = %d, want %d", i, got[i], want[i])
}
}
}
func TestHeapMaxOrder(t *testing.T) {
h := NewHeap[int](func(a, b int) bool { return a > b }) // 大顶堆
for _, v := range []int{5, 3, 8, 1, 4} {
h.Push(v)
}
top, _ := h.Peek()
if top != 8 {
t.Fatalf("大顶堆 Peek = %d, want 8", top)
}
var got []int
for !h.Empty() {
v, _ := h.Pop()
got = append(got, v)
}
want := []int{8, 5, 4, 3, 1}
for i := range want {
if got[i] != want[i] {
t.Fatalf("大顶堆 Pop[%d] = %d, want %d", i, got[i], want[i])
}
}
}
func TestHeapInit(t *testing.T) {
h := NewHeap[int](func(a, b int) bool { return a < b })
data := []int{9, 5, 2, 7, 1, 8, 3}
// Init 后 h 持有 data 的底层数组,Pop 会原地修改它。
// 所以 ref 必须在 Init 之前拷贝,避免被破坏。
ref := make([]int, len(data))
copy(ref, data)
sort.Ints(ref)
h.Init(data)
if top, _ := h.Peek(); top != 1 {
t.Fatalf("Init 后 Peek = %d, want 1", top)
}
var got []int
for !h.Empty() {
v, _ := h.Pop()
got = append(got, v)
}
for i := range ref {
if got[i] != ref[i] {
t.Fatalf("Init+Pop[%d] = %d, want %d", i, got[i], ref[i])
}
}
}
func TestHeapEmpty(t *testing.T) {
h := NewHeap[int](func(a, b int) bool { return a < b })
if _, ok := h.Pop(); ok {
t.Fatal("空堆 Pop 应返回 false")
}
if _, ok := h.Peek(); ok {
t.Fatal("空堆 Peek 应返回 false")
}
}
func TestHeapRandomVsSort(t *testing.T) {
// 随机数据:堆弹出顺序应与排序结果一致。
h := NewHeap[int](func(a, b int) bool { return a < b })
rng := rand.New(rand.NewSource(42))
const N = 2000
ref := make([]int, N)
for i := 0; i < N; i++ {
v := rng.Intn(10000)
h.Push(v)
ref[i] = v
}
sort.Ints(ref)
for i := 0; i < N; i++ {
v, ok := h.Pop()
if !ok || v != ref[i] {
t.Fatalf("随机测试[%d] = %d,%v, want %d", i, v, ok, ref[i])
}
}
}
func TestHeapString(t *testing.T) {
h := NewHeap[string](func(a, b string) bool { return a < b })
for _, s := range []string{"banana", "apple", "cherry"} {
h.Push(s)
}
first, _ := h.Pop()
if first != "apple" {
t.Fatalf("字符串堆 Pop = %q, want \"apple\"", first)
}
}
+99
View File
@@ -0,0 +1,99 @@
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 }
+86
View File
@@ -0,0 +1,86 @@
package ds
import "testing"
func TestLinkedListPushDelete(t *testing.T) {
l := NewLinkedList[int]()
if !l.Empty() {
t.Fatal("新建应为空")
}
l.PushBack(2)
l.PushBack(3)
l.PushFront(1)
l.PushFront(0)
// 期望 [0,1,2,3]
got := l.ToSlice()
want := []int{0, 1, 2, 3}
for i := range want {
if got[i] != want[i] {
t.Fatalf("ToSlice[%d] = %d, want %d", i, got[i], want[i])
}
}
// 删中间
if !l.Delete(2) {
t.Fatal("Delete(2) 应成功")
}
if l.Delete(99) {
t.Fatal("Delete(99) 不应成功")
}
// 删头部
if !l.Delete(0) {
t.Fatal("Delete(0) 应成功")
}
// 删尾部
if !l.Delete(3) {
t.Fatal("Delete(3) 应成功")
}
got = l.ToSlice()
if len(got) != 1 || got[0] != 1 {
t.Fatalf("删完后剩 %v, want [1]", got)
}
// 删到空,验证 tail 回到 head
if !l.Delete(1) {
t.Fatal("Delete(1) 应成功")
}
if !l.Empty() {
t.Fatal("应为空")
}
}
func TestLinkedListReverse(t *testing.T) {
l := NewLinkedList[string]()
for _, s := range []string{"a", "b", "c", "d"} {
l.PushBack(s)
}
l.Reverse()
got := l.ToSlice()
want := []string{"d", "c", "b", "a"}
for i := range want {
if got[i] != want[i] {
t.Fatalf("Reverse[%d] = %q, want %q", i, got[i], want[i])
}
}
// 反转空表不应 panic
l2 := NewLinkedList[int]()
l2.Reverse()
if !l2.Empty() {
t.Fatal("空表反转后应仍为空")
}
}
func TestLinkedListSingleNode(t *testing.T) {
l := NewLinkedList[int]()
l.PushBack(42)
if l.Len() != 1 {
t.Fatalf("Len = %d, want 1", l.Len())
}
if !l.Delete(42) {
t.Fatal("Delete 单节点应成功")
}
// 删空后再次 PushBack,验证 tail 指针正确
l.PushBack(7)
if got := l.ToSlice(); len(got) != 1 || got[0] != 7 {
t.Fatalf("删空后再插 = %v, want [7]", got)
}
}
+59
View File
@@ -0,0 +1,59 @@
package ds
// ds-queue.go
// 泛型切片队列。Enqueue O(1) 均摊,Dequeue O(1)(用 head 指针,避免每次头部搬运)。
// 当剩余元素不足时惰性整理底层数组,控制内存增长。
// Queue 泛型队列,零值可用。
type Queue[T any] struct {
data []T
head int // 队首索引,避免 O(n) 头部搬运
}
// Enqueue 入队。
func (q *Queue[T]) Enqueue(v T) {
q.data = append(q.data, v)
}
// Dequeue 出队;空队列返回零值和 false。
func (q *Queue[T]) Dequeue() (T, bool) {
var zero T
if q.head >= len(q.data) {
return zero, false
}
v := q.data[q.head]
q.data[q.head] = zero // 置零,释放引用
q.head++
// 当已出队部分超过总容量一半时,整理切片回收底层内存。
if q.head > 0 && q.head*2 >= cap(q.data) {
q.compact()
}
return v, true
}
// Front 查看队首,不移除。
func (q *Queue[T]) Front() (T, bool) {
var zero T
if q.head >= len(q.data) {
return zero, false
}
return q.data[q.head], true
}
// Len 队列元素个数。
func (q *Queue[T]) Len() int { return len(q.data) - q.head }
// Empty 是否为空。
func (q *Queue[T]) Empty() bool { return q.head >= len(q.data) }
// compact 回收已出队占用的底层内存。
func (q *Queue[T]) compact() {
n := q.Len()
if n == 0 {
q.data = q.data[:0]
} else {
copy(q.data, q.data[q.head:])
q.data = q.data[:n]
}
q.head = 0
}
+61
View File
@@ -0,0 +1,61 @@
package ds
import "testing"
func TestQueueFIFO(t *testing.T) {
var q Queue[int]
if !q.Empty() {
t.Fatal("新建队列应为空")
}
if _, ok := q.Dequeue(); ok {
t.Fatal("空队列 Dequeue 应返回 false")
}
q.Enqueue(1)
q.Enqueue(2)
q.Enqueue(3)
if q.Len() != 3 {
t.Fatalf("Len = %d, want 3", q.Len())
}
// FIFO 顺序
for _, want := range []int{1, 2, 3} {
got, ok := q.Dequeue()
if !ok {
t.Fatalf("Dequeue 失败,期望 %d", want)
}
if got != want {
t.Fatalf("Dequeue = %d, want %d", got, want)
}
}
if !q.Empty() {
t.Fatal("全部出队后应为空")
}
}
func TestQueueFront(t *testing.T) {
var q Queue[string]
q.Enqueue("x")
q.Enqueue("y")
f, ok := q.Front()
if !ok || f != "x" {
t.Fatalf("Front = %q,%v, want \"x\",true", f, ok)
}
_, _ = q.Dequeue()
f, _ = q.Front()
if f != "y" {
t.Fatalf("第二次 Front = %q, want \"y\"", f)
}
}
func TestQueueCompactNoGrowth(t *testing.T) {
// 大量入队/出队后,逻辑长度正确且不泄漏内存。
var q Queue[int]
for i := 0; i < 1000; i++ {
q.Enqueue(i)
_, _ = q.Dequeue()
}
if !q.Empty() {
t.Fatal("1000 次入队+出队后应为空")
}
}
+91
View File
@@ -0,0 +1,91 @@
package ds
// ds-set.go
// 泛型集合,基于 map[T]struct{}。T 约束为 comparable。
// Add/Remove/Contains 均 O(1) 均摊。支持并集/交集/差集。
// 比 map[T]bool 更省内存(空结构体不占空间),语义也更清晰。
// Set 泛型集合,零值不可直接用(map 零值不能写),必须用 NewSet。
type Set[T comparable] struct {
m map[T]struct{}
}
// NewSet 创建空集合。
func NewSet[T comparable]() *Set[T] {
return &Set[T]{m: make(map[T]struct{})}
}
// NewSetFrom 从切片创建集合,自动去重。
func NewSetFrom[T comparable](vals []T) *Set[T] {
s := NewSet[T]()
for _, v := range vals {
s.m[v] = struct{}{}
}
return s
}
// Add 添加元素,已存在则无操作。
func (s *Set[T]) Add(v T) { s.m[v] = struct{}{} }
// Remove 删除元素,不存在则无操作。
func (s *Set[T]) Remove(v T) { delete(s.m, v) }
// Contains 是否包含元素。
func (s *Set[T]) Contains(v T) bool {
_, ok := s.m[v]
return ok
}
// Len 元素个数。
func (s *Set[T]) Len() int { return len(s.m) }
// Empty 是否为空。
func (s *Set[T]) Empty() bool { return len(s.m) == 0 }
// ToSlice 转为切片。map 遍历顺序无序,如需稳定顺序需自行排序。
func (s *Set[T]) ToSlice() []T {
out := make([]T, 0, len(s.m))
for v := range s.m {
out = append(out, v)
}
return out
}
// Union 并集:返回新集合,包含 s 和 other 的所有元素。
func (s *Set[T]) Union(other *Set[T]) *Set[T] {
r := NewSet[T]()
for v := range s.m {
r.m[v] = struct{}{}
}
for v := range other.m {
r.m[v] = struct{}{}
}
return r
}
// Intersect 交集:返回新集合,仅包含 s 和 other 都有的元素。
func (s *Set[T]) Intersect(other *Set[T]) *Set[T] {
r := NewSet[T]()
// 遍历较小集合,降低常数
small, big := s, other
if small.Len() > big.Len() {
small, big = big, small
}
for v := range small.m {
if _, ok := big.m[v]; ok {
r.m[v] = struct{}{}
}
}
return r
}
// Difference 差集:返回新集合,包含在 s 中但不在 other 中的元素。
func (s *Set[T]) Difference(other *Set[T]) *Set[T] {
r := NewSet[T]()
for v := range s.m {
if _, ok := other.m[v]; !ok {
r.m[v] = struct{}{}
}
}
return r
}
+90
View File
@@ -0,0 +1,90 @@
package ds
import (
"reflect"
"sort"
"testing"
)
func TestSetBasic(t *testing.T) {
s := NewSet[int]()
if !s.Empty() {
t.Fatal("新建应为空")
}
s.Add(1)
s.Add(2)
s.Add(1) // 重复
if s.Len() != 2 {
t.Fatalf("Len = %d, want 2", s.Len())
}
if !s.Contains(1) {
t.Fatal("应包含 1")
}
if s.Contains(3) {
t.Fatal("不应包含 3")
}
s.Remove(1)
if s.Contains(1) {
t.Fatal("Remove 后不应再包含 1")
}
}
func TestSetFromSlice(t *testing.T) {
s := NewSetFrom([]string{"a", "b", "a", "c", "b"})
if s.Len() != 3 {
t.Fatalf("Len = %d, want 3", s.Len())
}
for _, want := range []string{"a", "b", "c"} {
if !s.Contains(want) {
t.Fatalf("应包含 %q", want)
}
}
}
func TestSetUnionIntersectDiff(t *testing.T) {
a := NewSetFrom([]int{1, 2, 3, 4})
b := NewSetFrom([]int{3, 4, 5, 6})
u := a.Union(b)
if !sameSet(u, NewSetFrom([]int{1, 2, 3, 4, 5, 6})) {
t.Fatalf("Union = %v, want {1,2,3,4,5,6}", sortedSlice(u))
}
i := a.Intersect(b)
if !sameSet(i, NewSetFrom([]int{3, 4})) {
t.Fatalf("Intersect = %v, want {3,4}", sortedSlice(i))
}
d := a.Difference(b)
if !sameSet(d, NewSetFrom([]int{1, 2})) {
t.Fatalf("Difference = %v, want {1,2}", sortedSlice(d))
}
}
func TestSetToSlice(t *testing.T) {
s := NewSetFrom([]int{3, 1, 2})
got := sortedSlice(s)
want := []int{1, 2, 3}
if !reflect.DeepEqual(got, want) {
t.Fatalf("ToSlice(sorted) = %v, want %v", got, want)
}
}
// sameSet 比较两个集合元素是否相同(忽略顺序)。
func sameSet[T comparable](a, b *Set[T]) bool {
if a.Len() != b.Len() {
return false
}
for v := range a.m {
if _, ok := b.m[v]; !ok {
return false
}
}
return true
}
func sortedSlice(s *Set[int]) []int {
out := s.ToSlice()
sort.Ints(out)
return out
}
+41
View File
@@ -0,0 +1,41 @@
package ds
// ds-stack.go
// 泛型切片栈。Push/Pop/Peek 均 O(1)。
// Pop 前先读值再缩切片,并把被弹出位置置零,避免底层数组持有引用导致内存泄漏。
// Stack 泛型栈,零值可用。
type Stack[T any] struct {
data []T
}
// Push 入栈。
func (s *Stack[T]) Push(v T) { s.data = append(s.data, v) }
// Pop 出栈;空栈返回零值和 false。
func (s *Stack[T]) Pop() (T, bool) {
var zero T
if len(s.data) == 0 {
return zero, false
}
n := len(s.data) - 1
v := s.data[n]
s.data[n] = zero // 置零,释放引用
s.data = s.data[:n]
return v, true
}
// Peek 查看栈顶,不移除;空栈返回零值和 false。
func (s *Stack[T]) Peek() (T, bool) {
var zero T
if len(s.data) == 0 {
return zero, false
}
return s.data[len(s.data)-1], true
}
// Len 栈元素个数。
func (s *Stack[T]) Len() int { return len(s.data) }
// Empty 是否为空。
func (s *Stack[T]) Empty() bool { return len(s.data) == 0 }
+74
View File
@@ -0,0 +1,74 @@
package ds
import "testing"
func TestStackBasic(t *testing.T) {
var s Stack[int]
if !s.Empty() {
t.Fatal("新建栈应为空")
}
if _, ok := s.Pop(); ok {
t.Fatal("空栈 Pop 应返回 false")
}
if _, ok := s.Peek(); ok {
t.Fatal("空栈 Peek 应返回 false")
}
s.Push(1)
s.Push(2)
s.Push(3)
if s.Len() != 3 {
t.Fatalf("Len = %d, want 3", s.Len())
}
// LIFO 顺序
for _, want := range []int{3, 2, 1} {
got, ok := s.Pop()
if !ok {
t.Fatalf("Pop 失败,期望 %d", want)
}
if got != want {
t.Fatalf("Pop = %d, want %d", got, want)
}
}
if !s.Empty() {
t.Fatal("全部弹出后应为空")
}
}
func TestStackStringType(t *testing.T) {
var s Stack[string]
s.Push("a")
s.Push("b")
top, ok := s.Peek()
if !ok || top != "b" {
t.Fatalf("Peek = %q,%v, want \"b\",true", top, ok)
}
v, _ := s.Pop()
if v != "b" {
t.Fatalf("Pop = %q, want \"b\"", v)
}
}
func TestStackLeakFunctional(t *testing.T) {
// 功能性验证:大量 push/pop 交替后仍正确,间接验证无状态泄漏。
var s Stack[int]
s.Push(1)
s.Push(2)
v, _ := s.Pop()
if v != 2 {
t.Fatalf("Pop = %d, want 2", v)
}
s.Push(3)
v, _ = s.Pop()
if v != 3 {
t.Fatalf("Pop = %d, want 3", v)
}
v, _ = s.Pop()
if v != 1 {
t.Fatalf("Pop = %d, want 1", v)
}
if !s.Empty() {
t.Fatal("应为空")
}
}
+169
View File
@@ -0,0 +1,169 @@
package ds
// ds-tree.go
// 泛型二叉搜索树 (BST)。T 约束为 Ordered,Insert/Search 均基于比较。
// 中序遍历天然有序;提供递归与迭代两种遍历入口。
// 不做自平衡,退化时退化为链表;leetcode 题目里常需要这种朴素 BST 作为基础。
// TreeNode 二叉树节点。
type TreeNode[T any] struct {
Val T
Left *TreeNode[T]
Right *TreeNode[T]
}
// BST 二叉搜索树。
type BST[T Ordered] struct {
Root *TreeNode[T]
}
// Ordered 约束:支持 < <= > >= 比较。
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 | ~string
}
// NewBST 创建空 BST。
func NewBST[T Ordered]() *BST[T] { return &BST[T]{} }
// Insert 按 BST 性质插入,重复值放右子树。
func (t *BST[T]) Insert(v T) {
t.Root = insertNode(t.Root, v)
}
func insertNode[T Ordered](node *TreeNode[T], v T) *TreeNode[T] {
if node == nil {
return &TreeNode[T]{Val: v}
}
if v < node.Val {
node.Left = insertNode(node.Left, v)
} else {
node.Right = insertNode(node.Right, v)
}
return node
}
// Search 查找值是否存在。
func (t *BST[T]) Search(v T) bool {
cur := t.Root
for cur != nil {
if v == cur.Val {
return true
}
if v < cur.Val {
cur = cur.Left
} else {
cur = cur.Right
}
}
return false
}
// InOrder 中序遍历(左→根→右),BST 上结果升序。递归版。
func (t *BST[T]) InOrder() []T {
var out []T
var walk func(*TreeNode[T])
walk = func(n *TreeNode[T]) {
if n == nil {
return
}
walk(n.Left)
out = append(out, n.Val)
walk(n.Right)
}
walk(t.Root)
return out
}
// PreOrder 前序遍历(根→左→右)。递归版。
func (t *BST[T]) PreOrder() []T {
var out []T
var walk func(*TreeNode[T])
walk = func(n *TreeNode[T]) {
if n == nil {
return
}
out = append(out, n.Val)
walk(n.Left)
walk(n.Right)
}
walk(t.Root)
return out
}
// PostOrder 后序遍历(左→右→根)。递归版。
func (t *BST[T]) PostOrder() []T {
var out []T
var walk func(*TreeNode[T])
walk = func(n *TreeNode[T]) {
if n == nil {
return
}
walk(n.Left)
walk(n.Right)
out = append(out, n.Val)
}
walk(t.Root)
return out
}
// LevelOrder 层序遍历 (BFS),借助上面定义的 Queue。
func (t *BST[T]) LevelOrder() []T {
if t.Root == nil {
return nil
}
var out []T
q := &Queue[*TreeNode[T]]{}
q.Enqueue(t.Root)
for !q.Empty() {
node, _ := q.Dequeue()
out = append(out, node.Val)
if node.Left != nil {
q.Enqueue(node.Left)
}
if node.Right != nil {
q.Enqueue(node.Right)
}
}
return out
}
// Height 树高(空树为 0,单节点为 1)。
func (t *BST[T]) Height() int {
var h func(*TreeNode[T]) int
h = func(n *TreeNode[T]) int {
if n == nil {
return 0
}
l := h(n.Left)
r := h(n.Right)
if l > r {
return l + 1
}
return r + 1
}
return h(t.Root)
}
// DFS 迭代版深度优先遍历(前序),用栈模拟。作为迭代遍历的示例。
func (t *BST[T]) DFS() []T {
if t.Root == nil {
return nil
}
var out []T
st := &Stack[*TreeNode[T]]{}
st.Push(t.Root)
for !st.Empty() {
node, _ := st.Pop()
out = append(out, node.Val)
// 先压右再压左,弹出顺序才是 根→左→右
if node.Right != nil {
st.Push(node.Right)
}
if node.Left != nil {
st.Push(node.Left)
}
}
return out
}
+95
View File
@@ -0,0 +1,95 @@
package ds
import "testing"
func TestBSTInsertInOrder(t *testing.T) {
tree := NewBST[int]()
vals := []int{5, 3, 8, 1, 4, 7, 9}
for _, v := range vals {
tree.Insert(v)
}
got := tree.InOrder()
want := []int{1, 3, 4, 5, 7, 8, 9}
if len(got) != len(want) {
t.Fatalf("InOrder 长度 = %d, want %d", len(got), len(want))
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("InOrder[%d] = %d, want %d", i, got[i], want[i])
}
}
}
func TestBSTSearch(t *testing.T) {
tree := NewBST[int]()
for _, v := range []int{5, 3, 8, 1} {
tree.Insert(v)
}
if !tree.Search(3) {
t.Fatal("Search(3) 应为 true")
}
if tree.Search(99) {
t.Fatal("Search(99) 应为 false")
}
if tree.Search(0) {
t.Fatal("Search(0) 应为 false")
}
}
func TestBSTTraversals(t *testing.T) {
tree := NewBST[int]()
// 构造:
// 4
// / \
// 2 6
// / \ / \
// 1 3 5 7
for _, v := range []int{4, 2, 6, 1, 3, 5, 7} {
tree.Insert(v)
}
pre := tree.PreOrder()
wantPre := []int{4, 2, 1, 3, 6, 5, 7}
for i := range wantPre {
if pre[i] != wantPre[i] {
t.Fatalf("PreOrder[%d] = %d, want %d", i, pre[i], wantPre[i])
}
}
post := tree.PostOrder()
wantPost := []int{1, 3, 2, 5, 7, 6, 4}
for i := range wantPost {
if post[i] != wantPost[i] {
t.Fatalf("PostOrder[%d] = %d, want %d", i, post[i], wantPost[i])
}
}
lvl := tree.LevelOrder()
wantLvl := []int{4, 2, 6, 1, 3, 5, 7}
for i := range wantLvl {
if lvl[i] != wantLvl[i] {
t.Fatalf("LevelOrder[%d] = %d, want %d", i, lvl[i], wantLvl[i])
}
}
dfs := tree.DFS()
// DFS 迭代版是前序,应与 PreOrder 相同
for i := range wantPre {
if dfs[i] != wantPre[i] {
t.Fatalf("DFS[%d] = %d, want %d", i, dfs[i], wantPre[i])
}
}
}
func TestBSTHeightAndEmpty(t *testing.T) {
tree := NewBST[int]()
if tree.Height() != 0 {
t.Fatal("空树高度应为 0")
}
tree.Insert(1)
if tree.Height() != 1 {
t.Fatal("单节点高度应为 1")
}
tree.Insert(2)
tree.Insert(3)
// 退化为链表,高度 3
if tree.Height() != 3 {
t.Fatalf("Height = %d, want 3 (退化链表)", tree.Height())
}
}
+109
View File
@@ -0,0 +1,109 @@
package ds
// ds-trie.go
// 前缀树 (Trie),支持任意字符串的插入/查找/前缀匹配/删除。
// 每个节点 26 个子指针(默认按小写字母 a-z 索引),isEnd 标记单词结尾。
// 如需支持更多字符集,可改用 map[rune]*TrieNode。
// TrieNode 前缀树节点。
type TrieNode struct {
children [26]*TrieNode
isEnd bool
}
// Trie 前缀树。默认只支持小写字母 a-z。
type Trie struct {
root *TrieNode
}
// NewTrie 创建空 Trie。
func NewTrie() *Trie {
return &Trie{root: &TrieNode{}}
}
// Insert 插入单词。仅支持 a-z,其他字符会 panic(用 rune 索引前先校验)。
func (t *Trie) Insert(word string) {
node := t.root
for _, ch := range word {
idx := ch - 'a'
if idx < 0 || idx >= 26 {
panic("trie: 只支持小写字母 a-z,收到非法字符")
}
if node.children[idx] == nil {
node.children[idx] = &TrieNode{}
}
node = node.children[idx]
}
node.isEnd = true
}
// Search 查找完整单词是否存在(必须 isEnd)。
func (t *Trie) Search(word string) bool {
node := t.find(word)
return node != nil && node.isEnd
}
// StartsWith 是否存在以 prefix 为前缀的单词。
func (t *Trie) StartsWith(prefix string) bool {
return t.find(prefix) != nil
}
// find 沿前缀走到对应节点,不存在返回 nil。
func (t *Trie) find(s string) *TrieNode {
node := t.root
for _, ch := range s {
idx := ch - 'a'
if idx < 0 || idx >= 26 {
return nil
}
node = node.children[idx]
if node == nil {
return nil
}
}
return node
}
// Delete 删除单词。仅当单词存在时删除,并回收不再被任何单词共享的节点。
// 返回是否删除成功(单词存在且被删除)。
func (t *Trie) Delete(word string) bool {
// 先确认单词存在,避免与回收语义混淆。
node := t.find(word)
if node == nil || !node.isEnd {
return false
}
t.deleteNode(t.root, word, 0)
return true
}
// deleteNode 递归回收:删除 word 沿途不再被需要的节点。
// 调用前需保证 word 存在。返回值:当前节点是否可被父节点回收。
func (t *Trie) deleteNode(node *TrieNode, word string, depth int) bool {
if node == nil {
return false
}
if depth == len(word) {
node.isEnd = false
return isEmpty(node)
}
idx := int(word[depth] - 'a')
if idx < 0 || idx >= 26 {
return false
}
child := node.children[idx]
if t.deleteNode(child, word, depth+1) {
node.children[idx] = nil
}
// 当前节点可回收:无子节点 且 非单词结尾
return isEmpty(node) && !node.isEnd
}
// isEmpty 节点是否无任何子节点。
func isEmpty(node *TrieNode) bool {
for i := 0; i < 26; i++ {
if node.children[i] != nil {
return false
}
}
return true
}
+90
View File
@@ -0,0 +1,90 @@
package ds
import "testing"
func TestTrieInsertSearch(t *testing.T) {
tr := NewTrie()
words := []string{"apple", "app", "april", "banana"}
for _, w := range words {
tr.Insert(w)
}
for _, w := range words {
if !tr.Search(w) {
t.Fatalf("Search(%q) 应为 true", w)
}
}
if tr.Search("appl") {
t.Fatal("Search(\"appl\") 应为 false")
}
if tr.Search("apples") {
t.Fatal("Search(\"apples\") 应为 false")
}
}
func TestTrieStartsWith(t *testing.T) {
tr := NewTrie()
for _, w := range []string{"apple", "app", "april"} {
tr.Insert(w)
}
if !tr.StartsWith("app") {
t.Fatal("StartsWith(\"app\") 应为 true")
}
if !tr.StartsWith("ap") {
t.Fatal("StartsWith(\"ap\") 应为 true")
}
if tr.StartsWith("b") {
t.Fatal("StartsWith(\"b\") 应为 false")
}
}
func TestTrieDelete(t *testing.T) {
tr := NewTrie()
tr.Insert("apple")
tr.Insert("app")
// 删 apple,app 应保留(共享前缀节点不能被回收)
if !tr.Delete("apple") {
t.Fatal("Delete(\"apple\") 应成功")
}
if tr.Search("apple") {
t.Fatal("Delete 后 Search(\"apple\") 应为 false")
}
if !tr.Search("app") {
t.Fatal("Search(\"app\") 仍应为 true (共享前缀)")
}
if !tr.StartsWith("app") {
t.Fatal("StartsWith(\"app\") 仍应为 true")
}
// 删不存在的单词
if tr.Delete("xyz") {
t.Fatal("Delete(\"xyz\") 应返回 false")
}
// 删最后一个单词,根的对应子树应被完全回收
tr.Delete("app")
if tr.Search("app") {
t.Fatal("Delete(\"app\") 后应搜不到")
}
}
func TestTrieDeleteNonWordPrefix(t *testing.T) {
tr := NewTrie()
tr.Insert("apple")
// "appl" 是前缀但不是完整单词,Delete 应返回 false
if tr.Delete("appl") {
t.Fatal("Delete(\"appl\") 非完整单词,应返回 false")
}
if !tr.Search("apple") {
t.Fatal("apple 不应受影响")
}
}
func TestTrieEmpty(t *testing.T) {
tr := NewTrie()
if tr.Search("anything") {
t.Fatal("空 Trie Search 应为 false")
}
if tr.StartsWith("a") {
t.Fatal("空 Trie StartsWith 应为 false")
}
}