u
This commit is contained in:
+243
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package misc
|
||||
|
||||
import "testing"
|
||||
|
||||
// 题目(推测题干)
|
||||
//
|
||||
// 给定两个长度为 n 的 0/1 数组 a 和 b。
|
||||
// 一次操作:选择 [L, R, V],满足 1 ≤ L ≤ R ≤ n,V ∈ {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 == b(need 为空):
|
||||
// - 要一次操作让 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
@@ -174,8 +174,7 @@ func HeapSort(nums []int) {
|
||||
return
|
||||
}
|
||||
// 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 {
|
||||
l, r := 2*i+1, 2*i+2
|
||||
largest := i
|
||||
@@ -206,7 +205,39 @@ func HeapSort(nums []int) {
|
||||
// ShellSort 希尔排序:按递减步长分组做插入排序,最后步长为 1 退化为插入排序。
|
||||
// 平均约 O(n^1.3),非稳定。代码短,比插入排序在乱序数据上快很多。
|
||||
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] 整数,统计频次后前缀和定位。
|
||||
@@ -271,9 +302,10 @@ func TestSortAlgos(t *testing.T) {
|
||||
// {"SelectionSort", SelectionSort},
|
||||
// {"InsertionSort", InsertionSort},
|
||||
// {"MergeSort", MergeSort},
|
||||
{"HeapSort", HeapSort},
|
||||
{"BucketSort", BucketSort},
|
||||
// {"HeapSort", HeapSort},
|
||||
// {"BucketSort", BucketSort},
|
||||
// {"ShellSort", ShellSort},
|
||||
{"ShellSortStandard", ShellSortStandard},
|
||||
// {"CountingSort", CountingSort},
|
||||
}
|
||||
for _, s := range sorters {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package top100liked
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
@@ -136,6 +137,53 @@ func topKFrequentBucket(nums []int, k int) []int {
|
||||
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.Interface(Len/Less/Swap + Push/Pop)
|
||||
// - Less 按 cnt 升序,堆顶是堆内频率最小者
|
||||
// - 遍历 m:heap.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。
|
||||
func equalIgnoreOrder(a, b []int) bool {
|
||||
if len(a) != len(b) {
|
||||
@@ -167,6 +215,7 @@ func TestTopKFrequent(t *testing.T) {
|
||||
}{
|
||||
{"heap", topKFrequentHeap},
|
||||
{"bucket", topKFrequentBucket},
|
||||
{"container-heap", topKFrequentContainerHeap},
|
||||
}
|
||||
for _, s := range solvers {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user