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
+243
View File
@@ -0,0 +1,243 @@
package avl
import "cmp"
// avl.go
// 泛型 AVL 自平衡二叉搜索树。
//
// 约定:空子树高度为 0,叶子高度为 1;不插入重复值。
// 每次插入或删除后,都要更新 Height,再按 LL/LR/RR/RL 四种情况重平衡。
// AVLNode AVL 树节点。Height 是以该节点为根的子树高度。
type AVLNode[T cmp.Ordered] struct {
Val T
Left *AVLNode[T]
Right *AVLNode[T]
Height int
}
// AVLTree AVL 自平衡二叉搜索树。
type AVLTree[T cmp.Ordered] struct {
Root *AVLNode[T]
}
// NewAVLTree 创建一棵空 AVL 树。
func NewAVLTree[T cmp.Ordered]() *AVLTree[T] {
return &AVLTree[T]{}
}
// Insert 插入 v;重复值不插入。
func (t *AVLTree[T]) Insert(v T) {
var insert func(node *AVLNode[T]) *AVLNode[T]
insert = func(node *AVLNode[T]) *AVLNode[T] {
if node == nil {
return &AVLNode[T]{Val: v, Height: 1}
}
if v == node.Val {
return node
} else if v < node.Val {
node.Left = insert(node.Left)
} else {
node.Right = insert(node.Right)
}
node.Height = t.calculateHeight(node)
return t.balancing(node)
}
t.Root = insert(t.Root)
}
func (t *AVLTree[T]) balancing(node *AVLNode[T]) *AVLNode[T] {
bf := t.calculateBF(node)
if bf < -1 {
// 右重 R
_bf := t.calculateBF(node.Right)
if _bf <= 0 {
// RR:左旋自身
return t.rotateLeft(node)
}
if _bf > 0 {
// RL: 右旋右孩子再左旋自身
node.Right = t.rotateRight(node.Right)
return t.rotateLeft(node)
}
}
if bf > 1 {
// 左重 L
_bf := t.calculateBF(node.Left)
if _bf >= 0 {
// LL: 右旋自身
return t.rotateRight(node)
}
if _bf < 0 {
// LR: 左旋左孩子再右旋自身
node.Left = t.rotateLeft(node.Left)
return t.rotateRight(node)
}
}
return node
}
func (t *AVLTree[T]) calculateHeight(node *AVLNode[T]) int {
if node == nil {
return 0
}
l, r := 0, 0
if node.Left != nil {
l = node.Left.Height
}
if node.Right != nil {
r = node.Right.Height
}
return max(l, r) + 1
}
func (t *AVLTree[T]) calculateBF(node *AVLNode[T]) int {
if node == nil {
return 0
}
leftHeight, rightHeight := 0, 0
if node.Left != nil {
leftHeight = node.Left.Height
}
if node.Right != nil {
rightHeight = node.Right.Height
}
bf := leftHeight - rightHeight
return bf
}
// rotateLeft 对 node 左旋,返回旋转后的新子树根。
//
// node right
// / \ / \
// A right → node C
// / \ / \
// B C A B
//
// B 的值介于 node 和 right 之间,因此旋转后要接到 node.Right。
func (t *AVLTree[T]) rotateLeft(node *AVLNode[T]) *AVLNode[T] {
rNode := node.Right
node.Right = rNode.Left
rNode.Left = node
node.Height = t.calculateHeight(node)
rNode.Height = t.calculateHeight(rNode)
return rNode
}
// rotateRight 对 node 右旋,返回旋转后的新子树根。
//
// node left
// / \ / \
// left C → A node
// / \ / \
// A B B C
//
// B 的值介于 left 和 node 之间,因此旋转后要接到 node.Left。
func (t *AVLTree[T]) rotateRight(node *AVLNode[T]) *AVLNode[T] {
lNode := node.Left
node.Left = lNode.Right
lNode.Right = node
node.Height = t.calculateHeight(node)
lNode.Height = t.calculateHeight(lNode)
return lNode
}
func (t *AVLTree[T]) Inorder() []T {
res := []T{}
var helper func(node *AVLNode[T])
helper = func(node *AVLNode[T]) {
if node == nil {
return
}
helper(node.Left)
res = append(res, node.Val)
helper(node.Right)
}
helper(t.Root)
return res
}
// Search 查找 v 是否存在。只读操作,不需要更新高度或旋转。
func (t *AVLTree[T]) Search(v T) bool {
node := t.Root
for node != nil {
if node.Val == v {
return true
}
if v < node.Val {
node = node.Left
} else {
node = node.Right
}
}
return false
}
// Height 返回整棵树的高度;空树为 0。
func (t *AVLTree[T]) Height() int {
if t.Root == nil {
return 0
}
return t.Root.Height
}
// Min 返回最小值及其是否存在。
func (t *AVLTree[T]) Min() (T, bool) {
node := t.Root
if node == nil {
var zero T
return zero, false
}
for node.Left != nil {
node = node.Left
}
return node.Val, true
}
// Max 返回最大值及其是否存在。
func (t *AVLTree[T]) Max() (T, bool) {
node := t.Root
if node == nil {
var zero T
return zero, false
}
for node.Right != nil {
node = node.Right
}
return node.Val, true
}
// Delete 删除 v;若 v 不存在,树保持不变。
func (t *AVLTree[T]) Delete(v T) {
var deleteNode func(node *AVLNode[T], target T) *AVLNode[T]
deleteNode = func(node *AVLNode[T], target T) *AVLNode[T] {
if node == nil {
return nil
}
if target < node.Val {
node.Left = deleteNode(node.Left, target)
} else if target > node.Val {
node.Right = deleteNode(node.Right, target)
} else {
if node.Left == nil && node.Right != nil {
return node.Right
}
if node.Left != nil && node.Right == nil {
return node.Left
}
if node.Left != nil && node.Right != nil {
n := node.Right
for n.Left != nil {
n = n.Left
}
node.Val = n.Val
node.Right = deleteNode(node.Right, n.Val)
} else {
return nil
}
}
node.Height = t.calculateHeight(node)
return t.balancing(node)
}
t.Root = deleteNode(t.Root, v)
}
+189
View File
@@ -0,0 +1,189 @@
package avl
import (
"reflect"
"testing"
)
func TestAVLBasicOperations(t *testing.T) {
tree := NewAVLTree[int]()
if tree.Height() != 0 {
t.Fatalf("空树 Height() = %d, want 0", tree.Height())
}
if _, ok := tree.Min(); ok {
t.Fatal("空树 Min() 的 ok = true, want false")
}
if _, ok := tree.Max(); ok {
t.Fatal("空树 Max() 的 ok = true, want false")
}
if tree.Search(1) {
t.Fatal("空树 Search(1) = true, want false")
}
for _, v := range []int{6, 3, 2, 1, 4, 5} {
tree.Insert(v)
}
if got, want := tree.Inorder(), []int{1, 2, 3, 4, 5, 6}; !reflect.DeepEqual(got, want) {
t.Fatalf("Inorder() = %v, want %v", got, want)
}
if !tree.Search(1) || !tree.Search(6) || tree.Search(7) {
t.Fatalf("Search 结果不正确:Search(1)=%v, Search(6)=%v, Search(7)=%v", tree.Search(1), tree.Search(6), tree.Search(7))
}
if got, ok := tree.Min(); !ok || got != 1 {
t.Fatalf("Min() = (%d, %v), want (1, true)", got, ok)
}
if got, ok := tree.Max(); !ok || got != 6 {
t.Fatalf("Max() = (%d, %v), want (6, true)", got, ok)
}
assertAVL(t, tree.Root, nil, nil)
}
func TestAVLDelete(t *testing.T) {
cases := []struct {
name string
values []int
delete int
want []int
wantMin *int
wantMax *int
}{
{
name: "delete only root leaf",
values: []int{10},
delete: 10,
want: []int{},
},
{
name: "delete root with one child",
values: []int{10, 5},
delete: 10,
want: []int{5},
wantMin: intPtr(5),
wantMax: intPtr(5),
},
{
name: "delete non-root leaf and update parent height",
values: []int{10, 5, 15},
delete: 5,
want: []int{10, 15},
wantMin: intPtr(10),
wantMax: intPtr(15),
},
{
name: "delete non-root with one child",
values: []int{10, 5, 15, 12},
delete: 15,
want: []int{5, 10, 12},
wantMin: intPtr(5),
wantMax: intPtr(12),
},
{
name: "delete root with two children successor is leaf",
values: []int{10, 5, 15, 12, 18},
delete: 10,
want: []int{5, 12, 15, 18},
wantMin: intPtr(5),
wantMax: intPtr(18),
},
{
name: "delete root with two children successor has right child",
values: []int{10, 5, 20, 15, 25, 17},
delete: 10,
want: []int{5, 15, 17, 20, 25},
wantMin: intPtr(5),
wantMax: intPtr(25),
},
{
name: "delete missing value leaves tree unchanged",
values: []int{10, 5, 20, 15, 25},
delete: 99,
want: []int{5, 10, 15, 20, 25},
wantMin: intPtr(5),
wantMax: intPtr(25),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
tree := NewAVLTree[int]()
for _, v := range tc.values {
tree.Insert(v)
}
tree.Delete(tc.delete)
if got := tree.Inorder(); !reflect.DeepEqual(got, tc.want) {
t.Fatalf("Delete(%d) 后 Inorder() = %v, want %v", tc.delete, got, tc.want)
}
if tree.Search(tc.delete) && tc.delete != 99 {
t.Fatalf("Delete(%d) 后 Search(%d) = true, want false", tc.delete, tc.delete)
}
if tc.wantMin == nil {
if tree.Height() != 0 {
t.Fatalf("删空树后 Height() = %d, want 0", tree.Height())
}
if _, ok := tree.Min(); ok {
t.Fatal("删空树后 Min() 的 ok = true, want false")
}
if _, ok := tree.Max(); ok {
t.Fatal("删空树后 Max() 的 ok = true, want false")
}
} else {
if got, ok := tree.Min(); !ok || got != *tc.wantMin {
t.Fatalf("Min() = (%d, %v), want (%d, true)", got, ok, *tc.wantMin)
}
if got, ok := tree.Max(); !ok || got != *tc.wantMax {
t.Fatalf("Max() = (%d, %v), want (%d, true)", got, ok, *tc.wantMax)
}
}
assertAVL(t, tree.Root, nil, nil)
})
}
}
func TestAVLDeleteRebalancesOnPathToRoot(t *testing.T) {
tree := NewAVLTree[int]()
for _, v := range []int{4, 2, 6, 1, 3, 5, 7} {
tree.Insert(v)
}
for _, v := range []int{7, 6, 5} {
tree.Delete(v)
assertAVL(t, tree.Root, nil, nil)
}
if got, want := tree.Inorder(), []int{1, 2, 3, 4}; !reflect.DeepEqual(got, want) {
t.Fatalf("连续删除后 Inorder() = %v, want %v", got, want)
}
}
func assertAVL(t *testing.T, node *AVLNode[int], lower, upper *int) int {
t.Helper()
if node == nil {
return 0
}
if lower != nil && node.Val <= *lower {
t.Fatalf("BST 性质被破坏:节点 %d <= 下界 %d", node.Val, *lower)
}
if upper != nil && node.Val >= *upper {
t.Fatalf("BST 性质被破坏:节点 %d >= 上界 %d", node.Val, *upper)
}
leftHeight := assertAVL(t, node.Left, lower, &node.Val)
rightHeight := assertAVL(t, node.Right, &node.Val, upper)
if balanceFactor := leftHeight - rightHeight; balanceFactor < -1 || balanceFactor > 1 {
t.Fatalf("节点 %d 的 BF = %d, want [-1, 1]", node.Val, balanceFactor)
}
wantHeight := max(leftHeight, rightHeight) + 1
if node.Height != wantHeight {
t.Fatalf("节点 %d 的 Height = %d, want %d", node.Val, node.Height, wantHeight)
}
return wantHeight
}
func intPtr(v int) *int {
return &v
}
+240
View File
@@ -0,0 +1,240 @@
package avl
import "fmt"
// AVL 自平衡二叉搜索树 —— 正确实现参考
//
// 核心设计点(对比你原文件里的 4 个 bug):
// 1. 节点维护 Height,平衡因子 BF = height(Left) - height(Right);合法范围 {-1,0,1}
// 2. 旋转函数【返回新子树根】,调用方用返回值接回父节点的 Left/Right
// —— 这解决了"旋转接不回去"的根本问题
// 3. 失衡判定看【子节点的 BF】,区分 LL/RR/LR/RL 四种 case,不是只看自己 BF
// 4. 不用全局 depthIncre flag,递归返回新根,height 直接从子节点算
type Node struct {
Val int
Left *Node
Right *Node
Height int // 以该节点为根的子树高度;空节点=0,叶子=1
}
func height(n *Node) int {
if n == nil {
return 0
}
return n.Height
}
func balanceFactor(n *Node) int {
if n == nil {
return 0
}
return height(n.Left) - height(n.Right)
}
func updateHeight(n *Node) {
l, r := height(n.Left), height(n.Right)
if l > r {
n.Height = l + 1
} else {
n.Height = r + 1
}
}
// 左旋:右子太重时,把右子 y 提上来,x 沉为 y 的左子
//
// x y
// / \ / \
// A y → x C
// / \ / \
// B C A B
//
// B 的值在 x、y 之间,必须留在中间 → 挂到 x 的右侧
func rotateLeft(x *Node) *Node {
y := x.Right
x.Right = y.Left
y.Left = x
updateHeight(x) // 先更新成为孩子的 x
updateHeight(y) // 再更新成为根的 y
return y
}
// 右旋:左子太重时,把左子 x 提上来,y 沉为 x 的右子
//
// y x
// / \ / \
// x C → A y
// / \ / \
// A B B C
//
// B 的值在 x、y 之间 → 挂到 y 的左侧
func rotateRight(y *Node) *Node {
x := y.Left
y.Left = x.Right
x.Right = y
updateHeight(y) // 先更新成为孩子的 y
updateHeight(x) // 再更新成为根的 x
return x
}
// 插入:递归返回新子树根
func Insert(root *Node, val int) *Node {
if root == nil {
return &Node{Val: val, Height: 1}
}
if val < root.Val {
root.Left = Insert(root.Left, val)
} else if val > root.Val {
root.Right = Insert(root.Right, val)
} else {
return root // 不允许重复值
}
updateHeight(root)
bf := balanceFactor(root)
// LL: 左子左重(左子 BF >= 0)→ 右旋一次
if bf > 1 && balanceFactor(root.Left) >= 0 {
return rotateRight(root)
}
// LR: 左子右重(左子 BF < 0)→ 先左旋左子,再右旋自己
if bf > 1 && balanceFactor(root.Left) < 0 {
root.Left = rotateLeft(root.Left)
return rotateRight(root)
}
// RR: 右子右重(右子 BF <= 0)→ 左旋一次
if bf < -1 && balanceFactor(root.Right) <= 0 {
return rotateLeft(root)
}
// RL: 右子左重(右子 BF > 0)→ 先右旋右子,再左旋自己
if bf < -1 && balanceFactor(root.Right) > 0 {
root.Right = rotateRight(root.Right)
return rotateLeft(root)
}
return root
}
// 删除:递归返回新子树根
func Delete(root *Node, val int) *Node {
if root == nil {
return nil
}
if val < root.Val {
root.Left = Delete(root.Left, val)
} else if val > root.Val {
root.Right = Delete(root.Right, val)
} else {
// 命中要删的节点
if root.Left == nil {
return root.Right
}
if root.Right == nil {
return root.Left
}
// 两子都在:用右子树最小值替换当前值,再删右子树里的那个最小值
minNode := root.Right
for minNode.Left != nil {
minNode = minNode.Left
}
root.Val = minNode.Val
root.Right = Delete(root.Right, minNode.Val)
}
updateHeight(root)
bf := balanceFactor(root)
// 重平衡逻辑与 Insert 完全一致(看子节点 BF 判 case)
if bf > 1 && balanceFactor(root.Left) >= 0 {
return rotateRight(root)
}
if bf > 1 && balanceFactor(root.Left) < 0 {
root.Left = rotateLeft(root.Left)
return rotateRight(root)
}
if bf < -1 && balanceFactor(root.Right) <= 0 {
return rotateLeft(root)
}
if bf < -1 && balanceFactor(root.Right) > 0 {
root.Right = rotateRight(root.Right)
return rotateLeft(root)
}
return root
}
func Search(root *Node, val int) bool {
for root != nil {
if val < root.Val {
root = root.Left
} else if val > root.Val {
root = root.Right
} else {
return true
}
}
return false
}
// 中序遍历 → 应得到升序序列(验证 BST 性质没被破坏)
func Inorder(root *Node, out *[]int) {
if root == nil {
return
}
Inorder(root.Left, out)
*out = append(*out, root.Val)
Inorder(root.Right, out)
}
func runAVLDemo() {
var root *Node
// 测试 1: 顺序插入 1..7(普通 BST 会退化成高度=7 的链表)
for _, v := range []int{1, 2, 3, 4, 5, 6, 7} {
root = Insert(root, v)
}
var arr []int
Inorder(root, &arr)
fmt.Println("顺序插入 1..7 中序遍历:", arr)
fmt.Printf("树高 = %d (普通BST会=7, AVL 应 ≤ 4)\n", height(root))
fmt.Println("Search 5:", Search(root, 5), "Search 99:", Search(root, 99))
// 测试 2: 乱序插入
root = nil
for _, v := range []int{5, 2, 8, 1, 9, 3, 7, 6, 4} {
root = Insert(root, v)
}
arr = nil
Inorder(root, &arr)
fmt.Println("\n乱序插入中序遍历:", arr)
fmt.Printf("树高 = %d\n", height(root))
// 测试 3: 删除
root = Delete(root, 5)
arr = nil
Inorder(root, &arr)
fmt.Println("删 5 后中序遍历:", arr)
fmt.Println("Search 5:", Search(root, 5))
fmt.Printf("树高 = %d\n", height(root))
// 测试 4: 验证所有 BF ∈ {-1,0,1}
if checkAllBF(root) {
fmt.Println("\n所有节点 |BF| ≤ 1 ✓")
}
}
// 验证整棵树每个节点的 |BF| ≤ 1
func checkAllBF(root *Node) bool {
var walk func(*Node) bool
walk = func(n *Node) bool {
if n == nil {
return true
}
bf := balanceFactor(n)
if bf < -1 || bf > 1 {
fmt.Printf("违反! 节点 %d 的 BF = %d\n", n.Val, bf)
return false
}
return walk(n.Left) && walk(n.Right)
}
return walk(root)
}
func main() {
runAVLDemo()
}
+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")
}
}
+109
View File
@@ -0,0 +1,109 @@
package misc
import "testing"
// 题目(推测题干)
//
// 给定两个长度为 n 的 0/1 数组 a 和 b。
// 一次操作:选择 [L, R, V],满足 1 ≤ L ≤ R ≤ nV ∈ {0,1}
// 将 a 中下标在 [L,R] 内的所有元素统一修改为 V(区间外不变)。
//
// 问:是否存在恰好一种操作使 a 变为 b?
// - 存在且唯一:输出该操作 [L,R,V](0-indexed 闭区间)
// - 不存在或不唯一:输出 -1
//
// 思路(0-indexed
//
// 记 need = { i : a[i] != b[i] },即必须被改到的位置。
//
// 1. 找 lo = min(need), hi = max(need)。need 为空 → a == b,特判(见下)。
// 2. 区间 [L,R] 必须 ⊇ [lo, hi],否则 need 中落在区间外的位置不会被改。
// 3. 区间内每个位置改完都要等于 b;而一个操作只有一个 V,
// 所以 [lo,hi] 内 b 必须全相同,V = b[lo]。否则无解 (-1)。
// 4. L 的取值范围:L ≤ lo。向左能扩张到 b 中连续等于 V 的位置
// (这些位置改后仍等于 b,不破坏)。
// - L 唯一 ⟺ lo == 0(到左边界)或 b[lo-1] != V(左边紧邻不等于 V,无法扩张)
// 5. R 的取值范围:R ≥ hi。向右能扩张到 b 中连续等于 V 的位置。
// - R 唯一 ⟺ hi == n-1(到右边界)或 b[hi+1] != V
// 6. L、R 都唯一 → 输出 [lo, hi, V];否则 -1。
//
// 特判 a == bneed 为空):
// - 要一次操作让 a 不变:区间内 a 必须全等于 V。
// - 任何单点 [i,i] 配 V=a[i] 都可行。
// - n == 1:唯一解 [0, 0, a[0]]。
// - n >= 2:有多个单点选择 → 不唯一 → -1。
func uniqueRangeAssign(a, b []int) (L, R, V int, ok bool) {
n := len(a)
// 第 1 步:找 lo, hi
lo, hi := -1, -1
for i := 0; i < n; i++ {
if a[i] != b[i] {
if lo == -1 {
lo = i
}
hi = i
}
}
// 特判 a == b
if lo == -1 {
if n == 1 {
return 0, 0, a[0], true
}
return 0, 0, 0, false
}
// 第 3 步:V = b[lo],检查 [lo,hi] 内 b 全等于 V
V = b[lo]
for i := lo; i <= hi; i++ {
if b[i] != V {
return 0, 0, 0, false
}
}
// 第 4 步:L 唯一性
lOK := lo == 0 || b[lo-1] != V
// 第 5 步:R 唯一性
rOK := hi == n-1 || b[hi+1] != V
if !lOK || !rOK {
return 0, 0, 0, false
}
// 第 6 步:唯一解
return lo, hi, V, true
}
func TestUniqueRangeAssign(t *testing.T) {
cases := []struct {
name string
a, b []int
wantL int
wantR int
wantV int
wantOK bool
}{
{"唯一-全改", []int{0, 0}, []int{1, 1}, 0, 1, 1, true},
{"唯一-部分改", []int{0, 0, 0, 0, 0}, []int{1, 1, 1, 0, 0}, 0, 2, 1, true},
{"唯一-右扩", []int{0, 0, 0, 0, 0}, []int{0, 0, 1, 1, 1}, 2, 4, 1, true},
{"无解-内部b不一致", []int{0, 1, 0}, []int{1, 0, 1}, 0, 0, 0, false},
{"不唯一-R可扩", []int{0, 1, 1}, []int{1, 1, 1}, 0, 0, 0, false},
{"不唯一-L可扩", []int{1, 0, 1}, []int{1, 1, 1}, 0, 0, 0, false},
{"不唯一-a=b,n>=2", []int{1, 1, 1}, []int{1, 1, 1}, 0, 0, 0, false},
{"唯一-a=b,n=1", []int{0}, []int{0}, 0, 0, 0, true},
{"唯一-单点改", []int{0}, []int{1}, 0, 0, 1, true},
{"唯一-覆盖整个数组", []int{1, 1, 0, 1}, []int{0, 0, 0, 0}, 0, 3, 0, true},
{"无解-need不连续且b不一致", []int{0, 0, 0, 0}, []int{1, 0, 0, 1}, 0, 0, 0, false},
}
for _, c := range cases {
L, R, V, ok := uniqueRangeAssign(c.a, c.b)
pass := ok == c.wantOK &&
(!ok || (L == c.wantL && R == c.wantR && V == c.wantV))
if !pass {
t.Errorf("%s: a=%v b=%v -> [%d,%d,%d] ok=%v, want [%d,%d,%d] ok=%v",
c.name, c.a, c.b, L, R, V, ok, c.wantL, c.wantR, c.wantV, c.wantOK)
}
}
}
+37 -5
View File
@@ -174,8 +174,7 @@ func HeapSort(nums []int) {
return return
} }
// siftDown:在 [0, end] 范围内把 nums[i] 下沉到正确位置 // siftDown:在 [0, end] 范围内把 nums[i] 下沉到正确位置
var siftDown func(i, end int) siftDown := func(i, end int) {
siftDown = func(i, end int) {
for 2*i+1 <= end { for 2*i+1 <= end {
l, r := 2*i+1, 2*i+2 l, r := 2*i+1, 2*i+2
largest := i largest := i
@@ -206,7 +205,39 @@ func HeapSort(nums []int) {
// ShellSort 希尔排序:按递减步长分组做插入排序,最后步长为 1 退化为插入排序。 // ShellSort 希尔排序:按递减步长分组做插入排序,最后步长为 1 退化为插入排序。
// 平均约 O(n^1.3),非稳定。代码短,比插入排序在乱序数据上快很多。 // 平均约 O(n^1.3),非稳定。代码短,比插入排序在乱序数据上快很多。
func ShellSort(nums []int) { func ShellSort(nums []int) {
// TODO: 你来写 gap := len(nums)
for gap > 1 {
gap /= 2
for i := 0; i < len(nums); i++ {
// 插入排序
for j := 1; j*gap+i < len(nums); j++ {
for k := j; k > 0; k-- {
if nums[k*gap+i] < nums[(k-1)*gap+i] {
nums[k*gap+i], nums[(k-1)*gap+i] = nums[(k-1)*gap+i], nums[k*gap+i]
} else {
break
}
}
}
}
}
}
// ShellSortStandard 是更常见的希尔排序写法:不显式遍历每一个分组,
// 而是让 j 每次减 gap,使其天然只在 nums[i%gap] 这一组内移动。
// 相比 ShellSort 的交换式插入,这里先保存 key、再右移元素,能减少写入次数。
func ShellSortStandard(nums []int) {
for gap := len(nums) / 2; gap > 0; gap /= 2 {
for i := gap; i < len(nums); i++ {
key := nums[i]
j := i
for j-gap >= 0 && key < nums[j-gap] {
nums[j] = nums[j-gap]
j -= gap
}
nums[j] = key
}
}
} }
// CountingSort 计数排序:元素为 [0, k] 整数,统计频次后前缀和定位。 // CountingSort 计数排序:元素为 [0, k] 整数,统计频次后前缀和定位。
@@ -271,9 +302,10 @@ func TestSortAlgos(t *testing.T) {
// {"SelectionSort", SelectionSort}, // {"SelectionSort", SelectionSort},
// {"InsertionSort", InsertionSort}, // {"InsertionSort", InsertionSort},
// {"MergeSort", MergeSort}, // {"MergeSort", MergeSort},
{"HeapSort", HeapSort}, // {"HeapSort", HeapSort},
{"BucketSort", BucketSort}, // {"BucketSort", BucketSort},
// {"ShellSort", ShellSort}, // {"ShellSort", ShellSort},
{"ShellSortStandard", ShellSortStandard},
// {"CountingSort", CountingSort}, // {"CountingSort", CountingSort},
} }
for _, s := range sorters { for _, s := range sorters {
+49
View File
@@ -1,6 +1,7 @@
package top100liked package top100liked
import ( import (
"container/heap"
"reflect" "reflect"
"sort" "sort"
"testing" "testing"
@@ -136,6 +137,53 @@ func topKFrequentBucket(nums []int, k int) []int {
return res return res
} }
// 解法三:container/heap 维护大小 k 的最小堆 — O(n log k) / O(n)
// 思路同解法一,但用标准库 container/heap 提供堆算法,自己只实现 heap.Interface。
//
// container/heap 两层 API(最容易踩坑的地方):
// - 你实现的 Push/Pop 只是"回调"Push 追加到切片末尾,Pop 删除末尾元素
// - 真正对外的是 heap.Push / heap.Pop:它们调你的 Push/Pop 并负责 siftUp/siftDown
// - Less 用 < 是小顶堆(堆顶最小),用 > 是大顶堆
// - Push/Pop 必须用指针接收者(要改 slice header);Len/Less/Swap 用值接收者即可
//
// 要点:
// - entryHeap []entry 实现 heap.InterfaceLen/Less/Swap + Push/Pop
// - Less 按 cnt 升序,堆顶是堆内频率最小者
// - 遍历 mheap.Push(h, entry{v,c})h.Len() > k 时 heap.Pop(h) 淘汰最小
// - 收集堆中剩余 k 个元素返回
type entryHeap []entry
func (h entryHeap) Len() int { return len(h) }
func (h entryHeap) Less(i, j int) bool { return h[i].cnt < h[j].cnt } // < 小顶堆
func (h entryHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
// Push/Pop 是给 heap 包调用的回调,只负责在切片末尾增删,不要加堆序逻辑
func (h *entryHeap) Push(x any) { *h = append(*h, x.(entry)) }
func (h *entryHeap) Pop() any {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
}
func topKFrequentContainerHeap(nums []int, k int) []int {
m := countFreq(nums)
h := &entryHeap{}
heap.Init(h)
for v, c := range m {
heap.Push(h, entry{v, c})
if h.Len() > k {
heap.Pop(h)
}
}
res := make([]int, 0, k)
for h.Len() > 0 {
res = append(res, heap.Pop(h).(entry).val)
}
return res
}
// equalIgnoreOrder 忽略顺序比较两个 []int。 // equalIgnoreOrder 忽略顺序比较两个 []int。
func equalIgnoreOrder(a, b []int) bool { func equalIgnoreOrder(a, b []int) bool {
if len(a) != len(b) { if len(a) != len(b) {
@@ -167,6 +215,7 @@ func TestTopKFrequent(t *testing.T) {
}{ }{
{"heap", topKFrequentHeap}, {"heap", topKFrequentHeap},
{"bucket", topKFrequentBucket}, {"bucket", topKFrequentBucket},
{"container-heap", topKFrequentContainerHeap},
} }
for _, s := range solvers { for _, s := range solvers {
t.Run(s.name, func(t *testing.T) { t.Run(s.name, func(t *testing.T) {
+134
View File
@@ -0,0 +1,134 @@
package top100liked
import (
"fmt"
"testing"
)
// https://leetcode.cn/problems/find-median-from-data-stream/?envType=study-plan-v2&envId=top-100-liked
type heap[T any] struct {
data []T
compareFunc func(T, T) bool
}
func NewHeap[T any](compareFunc func(T, T) bool) heap[T] {
return heap[T]{
compareFunc: compareFunc,
}
}
func (h *heap[T]) Up(idx int) {
for (idx-1)/2 >= 0 {
cur := h.data[idx]
p := h.data[(idx-1)/2]
if h.compareFunc(cur, p) {
h.data[idx], h.data[(idx-1)/2] = h.data[(idx-1)/2], h.data[idx]
idx = (idx - 1) / 2
} else {
break
}
}
}
func (h *heap[T]) Down(idx int) {
for {
l, r := 2*idx+1, 2*idx+2
m := idx
if l < len(h.data) && h.compareFunc(h.data[l], h.data[m]) {
m = l
}
if r < len(h.data) && h.compareFunc(h.data[r], h.data[m]) {
m = r
}
if m == idx {
break
}
h.data[idx], h.data[m] = h.data[m], h.data[idx]
idx = m
}
}
func (h *heap[T]) Push(val T) {
h.data = append(h.data, val)
h.Up(len(h.data) - 1)
}
func (h *heap[T]) Pop() T {
if len(h.data) <= 0 {
var zero T
return zero
}
old := h.data[0]
h.data[0] = h.data[len(h.data)-1]
h.data = h.data[:len(h.data)-1]
h.Down(0)
return old
}
func (h *heap[T]) Top() T {
if len(h.data) > 0 {
return h.data[0]
}
var zero T
return zero
}
func (h *heap[T]) Len() int {
return len(h.data)
}
type MedianFinder struct {
left heap[int] // 小
right heap[int] // 大
}
func Constructor() MedianFinder {
return MedianFinder{
left: NewHeap(func(i1, i2 int) bool { return i1 > i2 }),
right: NewHeap(func(i1, i2 int) bool { return i1 < i2 }),
}
}
func (this *MedianFinder) AddNum(num int) {
if this.left.Len() == 0 {
this.left.Push(num)
return
}
if num >= this.left.Top() {
this.right.Push(num)
} else {
this.left.Push(num)
}
if this.left.Len() > this.right.Len()+1 {
v := this.left.Pop()
this.right.Push(v)
}
if this.right.Len() > this.left.Len()+1 {
v := this.right.Pop()
this.left.Push(v)
}
}
func (this *MedianFinder) FindMedian() float64 {
switch (this.left.Len() + this.right.Len()) % 2 {
case 0:
return (float64(this.left.Top()) + float64(this.right.Top())) / 2
case 1:
if this.left.Len() > this.right.Len() {
return float64(this.left.Top())
} else {
return float64(this.right.Top())
}
}
return 0
}
func Test(t *testing.T) {
medianFinder := Constructor()
medianFinder.AddNum(1) // arr = [1]
medianFinder.AddNum(2) // arr = [1, 2]
fmt.Println(medianFinder.FindMedian()) // 返回 1.5 ((1 + 2) / 2)
medianFinder.AddNum(3) // arr[1, 2, 3]
fmt.Println(medianFinder.FindMedian()) // return 2.0
}