u
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 次入队+出队后应为空")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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("应为空")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user