diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..87f1df6
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,23 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+[*.go]
+indent_style = tab
+indent_size = 4
+
+[Makefile]
+indent_style = tab
+
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 2
+
+[*.md]
+indent_style = space
+indent_size = 4
\ No newline at end of file
diff --git a/README.md b/README.md
index 44e4a9c..9a9c58c 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[中文](README_CN.md)
+
DAO
![logo](assets/logo.png)
@@ -10,375 +12,363 @@
-### 简介
+### Description
+
+`DAO` is a library of generic-based data structures and algorithms that complements the standard library in terms of
+data containers and algorithms to simplify business development.
-`DAO` 是一个基于泛型的数据结构与算法库
+### Index
-### 目录
+- [Description](#description)
+- [Index](#index)
+- [Vector](#vector)
+ - [Unique](#unique)
+ - [Sort](#sort)
+ - [Filter](#filter)
+- [Heap](#heap)
+ - [N-Way Heap](#n-way-heap)
+ - [N-Way Indexed Heap](#n-way-indexed-heap)
+- [Stack](#stack)
+- [Queue](#queue)
+- [Deque](#deque)
+- [LinkedList](#linkedlist)
+- [RBTree](#rbtree)
+- [Dict](#dict)
+- [HashMap](#hashmap)
+- [Segment Tree](#segment-tree)
+- [Benchmark](#benchmark)
-- [简介](#简介)
-- [目录](#目录)
-- [动态数组](#动态数组)
- - [去重](#去重)
- - [排序](#排序)
- - [过滤](#过滤)
-- [堆](#堆)
- - [二叉堆](#二叉堆)
- - [四叉堆](#四叉堆)
- - [八叉堆](#八叉堆)
-- [栈](#栈)
-- [队列](#队列)
-- [双端队列](#双端队列)
-- [双向链表](#双向链表)
-- [红黑树](#红黑树)
-- [字典树](#字典树)
-- [哈希表](#哈希表)
-- [线段树](#线段树)
-- [基准测试](#基准测试)
-### 动态数组
+### Vector
-#### 去重
+#### Unique
```go
package main
import (
- "fmt"
- "github.com/lxzan/dao/vector"
+ "fmt"
+ "github.com/lxzan/dao/vector"
)
func main() {
- var v = vector.NewFromInts(1, 3, 5, 3)
- v.Uniq()
- fmt.Printf("%v", v.Elem())
+ var v = vector.NewFromInts(1, 3, 5, 3)
+ v.Unique()
+ fmt.Printf("%v", v.Elem())
}
```
-#### 排序
+#### Sort
```go
package main
import (
- "fmt"
- "github.com/lxzan/dao/vector"
+ "fmt"
+ "github.com/lxzan/dao/vector"
)
func main() {
- var v = vector.NewFromInts(1, 3, 5, 2, 4, 6)
- v.Sort()
- fmt.Printf("%v", v.Elem())
+ var v = vector.NewFromInts(1, 3, 5, 2, 4, 6)
+ v.Sort()
+ fmt.Printf("%v", v.Elem())
}
```
-#### 过滤
+#### Filter
```go
package main
import (
- "fmt"
- "github.com/lxzan/dao/vector"
+ "fmt"
+ "github.com/lxzan/dao/vector"
)
func main() {
- var v = vector.NewFromInts(1, 3, 5, 2, 4, 6)
- v.Filter(func(i int, v vector.Int) bool {
- return v.GetID()%2 == 0
- })
- fmt.Printf("%v", v.Elem())
+ var v = vector.NewFromInts(1, 3, 5, 2, 4, 6)
+ v.Filter(func(i int, v vector.Int) bool {
+ return v.GetID()%2 == 0
+ })
+ fmt.Printf("%v", v.Elem())
}
```
-### 堆
+### Heap
-**堆** 又称之为优先队列, 堆顶元素总是最大或最小的. 常用的是四叉堆, `Push/Pop` 性能较为均衡.
+**Heap**, also known as a priority queue, where the top element of the heap is always the largest or smallest. Commonly
+used is the quadruple heap, `Push/Pop` is more balanced. Using `y=pow(2,x)` as the number of forks, to speed up
+parent-child computation.
-#### 二叉堆
+#### N-Way Heap
```go
package main
import (
- "github.com/lxzan/dao/heap"
- "github.com/lxzan/dao/types/cmp"
+ "github.com/lxzan/dao/heap"
+ "github.com/lxzan/dao/types/cmp"
)
func main() {
- var h = heap.NewWithForks(heap.Binary, cmp.Less[int])
- h.Push(1)
- h.Push(3)
- h.Push(5)
- h.Push(2)
- h.Push(4)
- h.Push(6)
- for h.Len() > 0 {
- println(h.Pop())
- }
+ var h = heap.NewWithWays(heap.Binary, cmp.Less[int])
+ h.Push(1)
+ h.Push(3)
+ h.Push(5)
+ h.Push(2)
+ h.Push(4)
+ h.Push(6)
+ for h.Len() > 0 {
+ println(h.Pop())
+ }
}
```
-#### 四叉堆
-
-```go
-package main
-
-import (
- "github.com/lxzan/dao/heap"
- "github.com/lxzan/dao/types/cmp"
-)
-
-func main() {
- var h = heap.NewWithForks(heap.Quadratic, cmp.Less[int])
- h.Push(1)
- h.Push(3)
- h.Push(5)
- h.Push(2)
- h.Push(4)
- h.Push(6)
- for h.Len() > 0 {
- println(h.Pop())
- }
-}
+#### N-Way Indexed Heap
-```
-
-#### 八叉堆
+Extends the normal heap with update and delete functions, often used in time heap algorithms.
```go
package main
import (
- "github.com/lxzan/dao/heap"
- "github.com/lxzan/dao/types/cmp"
+ "github.com/lxzan/dao/heap"
)
func main() {
- var h = heap.NewWithForks(heap.Octal, cmp.Less[int])
- h.Push(1)
- h.Push(3)
- h.Push(5)
- h.Push(2)
- h.Push(4)
- h.Push(6)
- for h.Len() > 0 {
- println(h.Pop())
- }
+ var h = heap.NewIndexedHeap[int, struct{}](heap.Quadratic, func(a, b int) bool { return a > b })
+ h.Push(1, struct{}{})
+ h.Push(3, struct{}{})
+ h.Push(5, struct{}{})
+ h.Push(2, struct{}{})
+ h.Push(4, struct{}{})
+ h.Push(6, struct{}{})
+ h.DeleteByIndex(5)
+ h.UpdateKeyByIndex(3, 7)
+ for h.Len() > 0 {
+ println(h.Pop().Key())
+ }
}
```
-### 栈
+### Stack
-**栈** 先进后出 (`LIFO`) 的数据结构
+**Stack** Last in first out (`LIFO`) data structure
```go
package main
import (
- "github.com/lxzan/dao/stack"
+ "github.com/lxzan/dao/stack"
)
func main() {
- var s stack.Stack[int]
- s.Push(1)
- s.Push(3)
- s.Push(5)
- for s.Len() > 0 {
- println(s.Pop())
- }
+ var s stack.Stack[int]
+ s.Push(1)
+ s.Push(3)
+ s.Push(5)
+ for s.Len() > 0 {
+ println(s.Pop())
+ }
}
```
-### 队列
+### Queue
-**队列** 先进先出 (`FIFO`) 的数据结构. `dao/queue` 在全部元素弹出后会自动重置, 复用内存空间
+**Queue** First in first out (`FIFO`) data structure. The `dao/queue` is automatically reset when all elements are
+ejected, reusing memory space.
```go
package main
import (
- "github.com/lxzan/dao/queue"
+ "github.com/lxzan/dao/queue"
)
func main() {
- var s = queue.New[int](0)
- s.Push(1)
- s.Push(3)
- s.Push(5)
- for s.Len() > 0 {
- println(s.Pop())
- }
+ var s = queue.New[int](0)
+ s.Push(1)
+ s.Push(3)
+ s.Push(5)
+ for s.Len() > 0 {
+ println(s.Pop())
+ }
}
```
-### 双端队列
+### Deque
-**双端队列** 类似于双向链表, 两端均可高效执行插入删除操作.
+**Deque** are similar to doubly linked list, where insertion and deletion operations can be
+performed efficiently at both ends.
-`dao/deque` 基于数组下标模拟指针实现, 删除后的空间后续仍可复用, 且不依赖 `sync.Pool`
+`dao/deque` is based on array subscripts to emulate pointers, the deleted space can still be reused later, and does not
+depend on `sync.Pool`.
```go
package main
import (
- "fmt"
- "github.com/lxzan/dao/deque"
+ "fmt"
+ "github.com/lxzan/dao/deque"
)
func main() {
- var list = deque.New[int](8)
- list.PushBack(1)
- list.PushBack(3)
- list.PushBack(5)
- list.PushBack(7)
- list.PushBack(9)
- for i := list.Front(); i != nil; i = list.Get(i.Next()) {
- fmt.Printf("%v ", i.Value())
- }
-
- println()
- for i := list.Back(); i != nil; i = list.Get(i.Prev()) {
- fmt.Printf("%v ", i.Value())
- }
+ var list = deque.New[int](8)
+ list.PushBack(1)
+ list.PushBack(3)
+ list.PushBack(5)
+ list.PushBack(7)
+ list.PushBack(9)
+ for i := list.Front(); i != nil; i = list.Get(i.Next()) {
+ fmt.Printf("%v ", i.Value())
+ }
+
+ println()
+ for i := list.Back(); i != nil; i = list.Get(i.Prev()) {
+ fmt.Printf("%v ", i.Value())
+ }
}
```
-### 双向链表
+### LinkedList
```go
package main
import (
- "fmt"
- "github.com/lxzan/dao/linkedlist"
+ "fmt"
+ "github.com/lxzan/dao/linkedlist"
)
func main() {
- var list = linkedlist.New[int]()
- list.PushBack(1)
- list.PushBack(3)
- list.PushBack(5)
- list.PushBack(7)
- list.PushBack(9)
- for i := list.Front(); i != nil; i = i.Next() {
- fmt.Printf("%v ", i.Value)
- }
-
- println()
- for i := list.Back(); i != nil; i = i.Prev() {
- fmt.Printf("%v ", i.Value)
- }
+ var list = linkedlist.New[int]()
+ list.PushBack(1)
+ list.PushBack(3)
+ list.PushBack(5)
+ list.PushBack(7)
+ list.PushBack(9)
+ for i := list.Front(); i != nil; i = i.Next() {
+ fmt.Printf("%v ", i.Value)
+ }
+
+ println()
+ for i := list.Back(); i != nil; i = i.Prev() {
+ fmt.Printf("%v ", i.Value)
+ }
}
```
-### 红黑树
+### RBTree
-高性能红黑树实现, 可作为内存数据库使用.
+A high-performance red-black tree implementation that can be used as an in-memory database.
```go
package main
import (
- "github.com/lxzan/dao/rbtree"
+ "github.com/lxzan/dao/rbtree"
)
func main() {
- var tree = rbtree.New[int, struct{}]()
- for i := 0; i < 10; i++ {
- tree.Set(i, struct{}{})
- }
-
- var results = tree.
- NewQuery().
- Left(func(key int) bool { return key >= 3 }).
- Right(func(key int) bool { return key <= 5 }).
- Order(rbtree.ASC).
- FindAll()
- for _, item := range results {
- println(item.Key)
- }
+ var tree = rbtree.New[int, struct{}]()
+ for i := 0; i < 10; i++ {
+ tree.Set(i, struct{}{})
+ }
+
+ var results = tree.
+ NewQuery().
+ Left(func(key int) bool { return key >= 3 }).
+ Right(func(key int) bool { return key <= 5 }).
+ Order(rbtree.ASC).
+ FindAll()
+ for _, item := range results {
+ println(item.Key)
+ }
}
```
-### 字典树
+### Dict
-**字典树** 又叫前缀树, 可以高效匹配字符串前缀. `dao/dict` 可以动态配置槽位宽度(由索引控制).
+**Dict** aka prefix tree, efficiently matches string prefixes. `dao/dict` can dynamically configure the width
+of slots (controlled by the index).
-注意: 合理设置索引, 超出索引长度的字符不能被索引优化
+Note: Set the index reasonably, characters beyond the index length can not be optimized for indexing.
```go
package main
import (
- "github.com/lxzan/dao/dict"
+ "github.com/lxzan/dao/dict"
)
func main() {
- var tree = dict.New[int]()
- tree.Set("listen", 1)
- tree.Set("list", 2)
- tree.Set("often", 3)
- tree.Set("oh!", 4)
- tree.Set("haha", 5)
- tree.Set("", 6)
-
- tree.Match("list", func(key string, value int) bool {
- println(key, value)
- return true
- })
+ var tree = dict.New[int]()
+ tree.Set("listen", 1)
+ tree.Set("list", 2)
+ tree.Set("often", 3)
+ tree.Set("oh!", 4)
+ tree.Set("haha", 5)
+ tree.Set("", 6)
+
+ tree.Match("list", func(key string, value int) bool {
+ println(key, value)
+ return true
+ })
}
```
-### 哈希表
+### HashMap
+
+An alias for `Runtime Map`, extending `Keys`, `Values`, `Range` and other utility methods.
```go
package main
import (
- "github.com/lxzan/dao/hashmap"
+ "github.com/lxzan/dao/hashmap"
)
func main() {
- var m = hashmap.New[string, int](8)
- m.Set("a", 1)
- m.Set("b", 2)
- m.Set("c", 3)
- m.Range(func(key string, val int) bool {
- println(key, val)
- return true
- })
+ var m = hashmap.New[string, int](8)
+ m.Set("a", 1)
+ m.Set("b", 2)
+ m.Set("c", 3)
+ m.Range(func(key string, val int) bool {
+ println(key, val)
+ return true
+ })
}
```
-### 线段树
+### Segment Tree
-**线段树** 是一种二叉树,它的每个节点都表示一个区间。 线段树的特点是可以在`O(logn)`的时间内进行区间查询和区间更新。
+**Segment Tree** is a binary tree in which each node represents an interval. Line segment trees are characterized by the
+ability to perform interval queries and interval updates in `O(logn)` time.
```go
package main
import (
- tree "github.com/lxzan/dao/segment_tree"
+ tree "github.com/lxzan/dao/segment_tree"
)
func main() {
- var data = []tree.Int64{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}
- var lines = tree.New[tree.Int64Schema, tree.Int64](data)
- var result = lines.Query(0, 10)
- println(result.MinValue, result.MaxValue, result.Sum)
+ var data = []tree.Int64{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}
+ var lines = tree.New[tree.Int64Schema, tree.Int64](data)
+ var result = lines.Query(0, 10)
+ println(result.MinValue, result.MaxValue, result.Sum)
}
```
-### 基准测试
+### Benchmark
- 1,000 elements
diff --git a/README_CN.md b/README_CN.md
new file mode 100644
index 0000000..763c0c4
--- /dev/null
+++ b/README_CN.md
@@ -0,0 +1,431 @@
+
+
DAO
+
![logo](assets/logo.png)
+
道生一, 一生二, 二生三, 三生万物; 万物负阴而抱阳, 冲气以为和
+
+
+
+
+[![Build Status](https://github.com/lxzan/dao/workflows/Go%20Test/badge.svg?branch=main)](https://github.com/lxzan/dao/actions?query=branch%3Amain) [![codecov](https://codecov.io/gh/lxzan/dao/graph/badge.svg?token=BQM1JHCDEE)](https://codecov.io/gh/lxzan/dao) [![go-version](https://img.shields.io/badge/go-%3E%3D1.18-30dff3?style=flat-square&logo=go)](https://github.com/lxzan/dao) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
+
+
+
+### 简介
+
+`DAO` 是一个基于泛型的数据结构与算法库, 补充标准库在数据容器和算法方面的不足, 简化业务开发.
+
+### 目录
+
+- [简介](#简介)
+- [目录](#目录)
+- [动态数组](#动态数组)
+ - [去重](#去重)
+ - [排序](#排序)
+ - [过滤](#过滤)
+- [堆](#堆)
+ - [N叉堆](#n叉堆)
+ - [N叉索引堆](#n叉索引堆)
+- [栈](#栈)
+- [队列](#队列)
+- [双端队列](#双端队列)
+- [双向链表](#双向链表)
+- [红黑树](#红黑树)
+- [字典树](#字典树)
+- [哈希表](#哈希表)
+- [线段树](#线段树)
+- [基准测试](#基准测试)
+
+### 动态数组
+
+#### 去重
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/lxzan/dao/vector"
+)
+
+func main() {
+ var v = vector.NewFromInts(1, 3, 5, 3)
+ v.Unique()
+ fmt.Printf("%v", v.Elem())
+}
+
+```
+
+#### 排序
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/lxzan/dao/vector"
+)
+
+func main() {
+ var v = vector.NewFromInts(1, 3, 5, 2, 4, 6)
+ v.Sort()
+ fmt.Printf("%v", v.Elem())
+}
+
+```
+
+#### 过滤
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/lxzan/dao/vector"
+)
+
+func main() {
+ var v = vector.NewFromInts(1, 3, 5, 2, 4, 6)
+ v.Filter(func(i int, v vector.Int) bool {
+ return v.GetID()%2 == 0
+ })
+ fmt.Printf("%v", v.Elem())
+}
+
+```
+
+### 堆
+
+**堆** 又称之为优先队列, 堆顶元素总是最大或最小的. 常用的是四叉堆, `Push/Pop` 性能较为均衡. 使用 `y=pow(2,x)` 作为分叉数,
+加快计算父子节点速度.
+
+#### N叉堆
+
+```go
+package main
+
+import (
+ "github.com/lxzan/dao/heap"
+ "github.com/lxzan/dao/types/cmp"
+)
+
+func main() {
+ var h = heap.NewWithWays(heap.Binary, cmp.Less[int])
+ h.Push(1)
+ h.Push(3)
+ h.Push(5)
+ h.Push(2)
+ h.Push(4)
+ h.Push(6)
+ for h.Len() > 0 {
+ println(h.Pop())
+ }
+}
+
+```
+
+#### N叉索引堆
+
+在普通堆的基础上拓展了更新和删除功能, 常用于时间堆算法.
+
+```go
+package main
+
+import (
+ "github.com/lxzan/dao/heap"
+)
+
+func main() {
+ var h = heap.NewIndexedHeap[int, struct{}](heap.Quadratic, func(a, b int) bool { return a > b })
+ h.Push(1, struct{}{})
+ h.Push(3, struct{}{})
+ h.Push(5, struct{}{})
+ h.Push(2, struct{}{})
+ h.Push(4, struct{}{})
+ h.Push(6, struct{}{})
+ h.DeleteByIndex(5)
+ h.UpdateKeyByIndex(3, 7)
+ for h.Len() > 0 {
+ println(h.Pop().Key())
+ }
+}
+
+```
+
+### 栈
+
+**栈** 后进先出 (`LIFO`) 的数据结构
+
+```go
+package main
+
+import (
+ "github.com/lxzan/dao/stack"
+)
+
+func main() {
+ var s stack.Stack[int]
+ s.Push(1)
+ s.Push(3)
+ s.Push(5)
+ for s.Len() > 0 {
+ println(s.Pop())
+ }
+}
+```
+
+### 队列
+
+**队列** 先进先出 (`FIFO`) 的数据结构. `dao/queue` 在全部元素弹出后会自动重置, 复用内存空间
+
+```go
+package main
+
+import (
+ "github.com/lxzan/dao/queue"
+)
+
+func main() {
+ var s = queue.New[int](0)
+ s.Push(1)
+ s.Push(3)
+ s.Push(5)
+ for s.Len() > 0 {
+ println(s.Pop())
+ }
+}
+
+```
+
+### 双端队列
+
+**双端队列** 类似于双向链表, 两端均可高效执行插入删除操作.
+
+`dao/deque` 基于数组下标模拟指针实现, 删除后的空间后续仍可复用, 且不依赖 `sync.Pool`
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/lxzan/dao/deque"
+)
+
+func main() {
+ var list = deque.New[int](8)
+ list.PushBack(1)
+ list.PushBack(3)
+ list.PushBack(5)
+ list.PushBack(7)
+ list.PushBack(9)
+ for i := list.Front(); i != nil; i = list.Get(i.Next()) {
+ fmt.Printf("%v ", i.Value())
+ }
+
+ println()
+ for i := list.Back(); i != nil; i = list.Get(i.Prev()) {
+ fmt.Printf("%v ", i.Value())
+ }
+}
+```
+
+### 双向链表
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/lxzan/dao/linkedlist"
+)
+
+func main() {
+ var list = linkedlist.New[int]()
+ list.PushBack(1)
+ list.PushBack(3)
+ list.PushBack(5)
+ list.PushBack(7)
+ list.PushBack(9)
+ for i := list.Front(); i != nil; i = i.Next() {
+ fmt.Printf("%v ", i.Value)
+ }
+
+ println()
+ for i := list.Back(); i != nil; i = i.Prev() {
+ fmt.Printf("%v ", i.Value)
+ }
+}
+```
+
+### 红黑树
+
+高性能红黑树实现, 可作为内存数据库使用.
+
+```go
+package main
+
+import (
+ "github.com/lxzan/dao/rbtree"
+)
+
+func main() {
+ var tree = rbtree.New[int, struct{}]()
+ for i := 0; i < 10; i++ {
+ tree.Set(i, struct{}{})
+ }
+
+ var results = tree.
+ NewQuery().
+ Left(func(key int) bool { return key >= 3 }).
+ Right(func(key int) bool { return key <= 5 }).
+ Order(rbtree.ASC).
+ FindAll()
+ for _, item := range results {
+ println(item.Key)
+ }
+}
+
+```
+
+### 字典树
+
+**字典树** 又叫前缀树, 可以高效匹配字符串前缀. `dao/dict` 可以动态配置槽位宽度(由索引控制).
+
+注意: 合理设置索引, 超出索引长度的字符不能被索引优化
+
+```go
+package main
+
+import (
+ "github.com/lxzan/dao/dict"
+)
+
+func main() {
+ var tree = dict.New[int]()
+ tree.Set("listen", 1)
+ tree.Set("list", 2)
+ tree.Set("often", 3)
+ tree.Set("oh!", 4)
+ tree.Set("haha", 5)
+ tree.Set("", 6)
+
+ tree.Match("list", func(key string, value int) bool {
+ println(key, value)
+ return true
+ })
+}
+```
+
+### 哈希表
+
+`Runtime Map` 的别名, 拓展了 `Keys`, `Values`, `Range` 等实用方法.
+
+```go
+package main
+
+import (
+ "github.com/lxzan/dao/hashmap"
+)
+
+func main() {
+ var m = hashmap.New[string, int](8)
+ m.Set("a", 1)
+ m.Set("b", 2)
+ m.Set("c", 3)
+ m.Range(func(key string, val int) bool {
+ println(key, val)
+ return true
+ })
+}
+```
+
+### 线段树
+
+**线段树** 是一种二叉树,它的每个节点都表示一个区间。 线段树的特点是可以在`O(logn)`的时间内进行区间查询和区间更新。
+
+```go
+package main
+
+import (
+ tree "github.com/lxzan/dao/segment_tree"
+)
+
+func main() {
+ var data = []tree.Int64{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}
+ var lines = tree.New[tree.Int64Schema, tree.Int64](data)
+ var result = lines.Query(0, 10)
+ println(result.MinValue, result.MaxValue, result.Sum)
+}
+
+```
+
+### 基准测试
+
+- 1,000 elements
+
+```
+go test -benchmem -bench '^Benchmark' ./benchmark/
+goos: windows
+goarch: amd64
+pkg: github.com/lxzan/dao/benchmark
+cpu: AMD Ryzen 5 PRO 4650G with Radeon Graphics
+BenchmarkDict_Set-12 8647 124.1 ns/op 48190 B/op 1003 allocs/op
+BenchmarkDict_Get-12 9609 122.0 ns/op 48000 B/op 1000 allocs/op
+BenchmarkDict_Match-12 3270 349.3 ns/op 48000 B/op 1000 allocs/op
+BenchmarkHeap_Push_Binary-12 55809 21.0 ns/op 38912 B/op 3 allocs/op
+BenchmarkHeap_Push_Quadratic-12 65932 18.0 ns/op 38912 B/op 3 allocs/op
+BenchmarkHeap_Push_Octal-12 71005 16.1 ns/op 38912 B/op 3 allocs/op
+BenchmarkHeap_Pop_Binary-12 10000 100.1 ns/op 16384 B/op 1 allocs/op
+BenchmarkHeap_Pop_Quadratic-12 10000 100.7 ns/op 16384 B/op 1 allocs/op
+BenchmarkHeap_Pop_Octal-12 9681 124.9 ns/op 16384 B/op 1 allocs/op
+BenchmarkStdList_Push-12 24715 48.7 ns/op 54000 B/op 1745 allocs/op
+BenchmarkStdList_PushAndPop-12 22006 54.7 ns/op 54000 B/op 1745 allocs/op
+BenchmarkLinkedList_Push-12 38464 31.7 ns/op 24000 B/op 1000 allocs/op
+BenchmarkLinkedList_PushAndPop-12 36898 32.6 ns/op 24000 B/op 1000 allocs/op
+BenchmarkDeque_Push-12 100468 11.7 ns/op 24576 B/op 1 allocs/op
+BenchmarkDeque_PushAndPop-12 51649 21.7 ns/op 37496 B/op 12 allocs/op
+BenchmarkRBTree_Set-12 9999 113.9 ns/op 72048 B/op 2001 allocs/op
+BenchmarkRBTree_Get-12 51806 22.7 ns/op 0 B/op 0 allocs/op
+BenchmarkRBTree_FindAll-12 2808 421.3 ns/op 288001 B/op 5000 allocs/op
+BenchmarkRBTree_FindAOne-12 4722 252.2 ns/op 56000 B/op 5000 allocs/op
+BenchmarkSegmentTree_Query-12 7498 164.4 ns/op 20 B/op 0 allocs/op
+BenchmarkSegmentTree_Update-12 10000 108.6 ns/op 15 B/op 0 allocs/op
+BenchmarkSort_Quick-12 24488 48.5 ns/op 0 B/op 0 allocs/op
+BenchmarkSort_Std-12 21703 54.9 ns/op 8216 B/op 2 allocs/op
+PASS
+ok github.com/lxzan/dao/benchmark 31.100s
+```
+
+- 1,000,000 elements
+
+```
+go test -benchmem -bench '^Benchmark' ./benchmark/
+goos: windows
+goarch: amd64
+pkg: github.com/lxzan/dao/benchmark
+cpu: AMD Ryzen 5 PRO 4650G with Radeon Graphics
+BenchmarkDict_Set-12 1 2295.2 ns/op 1405087408 B/op 24868109 allocs/op
+BenchmarkDict_Get-12 2 784.0 ns/op 48000000 B/op 1000000 allocs/op
+BenchmarkDict_Match-12 2 961.0 ns/op 48000000 B/op 1000000 allocs/op
+BenchmarkHeap_Push_Binary-12 48 24.8 ns/op 65708034 B/op 5 allocs/op
+BenchmarkHeap_Push_Quadratic-12 58 19.4 ns/op 65708033 B/op 5 allocs/op
+BenchmarkHeap_Push_Octal-12 69 17.1 ns/op 65708033 B/op 5 allocs/op
+BenchmarkHeap_Pop_Binary-12 3 376.3 ns/op 16007168 B/op 1 allocs/op
+BenchmarkHeap_Pop_Quadratic-12 3 342.8 ns/op 16007168 B/op 1 allocs/op
+BenchmarkHeap_Pop_Octal-12 3 374.8 ns/op 16007168 B/op 1 allocs/op
+BenchmarkStdList_Push-12 21 55.0 ns/op 55998007 B/op 1999745 allocs/op
+BenchmarkStdList_PushAndPop-12 15 67.5 ns/op 55998008 B/op 1999745 allocs/op
+BenchmarkLinkedList_Push-12 43 29.5 ns/op 24000000 B/op 1000000 allocs/op
+BenchmarkLinkedList_PushAndPop-12 39 34.7 ns/op 24000002 B/op 1000000 allocs/op
+BenchmarkDeque_Push-12 123 9.4 ns/op 24002560 B/op 1 allocs/op
+BenchmarkDeque_PushAndPop-12 60 18.7 ns/op 45098876 B/op 37 allocs/op
+BenchmarkRBTree_Set-12 6 171.9 ns/op 72000064 B/op 2000001 allocs/op
+BenchmarkRBTree_Get-12 22 50.1 ns/op 0 B/op 0 allocs/op
+BenchmarkRBTree_FindAll-12 1 1936.8 ns/op 288000128 B/op 5000001 allocs/op
+BenchmarkRBTree_FindAOne-12 1 1793.4 ns/op 56000000 B/op 5000000 allocs/op
+BenchmarkSegmentTree_Query-12 1 1630.0 ns/op 169678048 B/op 2000038 allocs/op
+BenchmarkSegmentTree_Update-12 1 1025.0 ns/op 169678048 B/op 2000038 allocs/op
+BenchmarkSort_Quick-12 10 109.5 ns/op 8003584 B/op 1 allocs/op
+BenchmarkSort_Std-12 9 123.0 ns/op 8003608 B/op 2 allocs/op
+PASS
+ok github.com/lxzan/dao/benchmark 47.376s
+```
diff --git a/benchmark/heap_test.go b/benchmark/heap_test.go
index f8cedce..6521b53 100644
--- a/benchmark/heap_test.go
+++ b/benchmark/heap_test.go
@@ -8,7 +8,7 @@ import (
)
func BenchmarkHeap_Push_Binary(b *testing.B) {
- var tpl = heap.NewWithForks(heap.Binary, cmp.Less[int])
+ var tpl = heap.NewWithWays(heap.Binary, cmp.Less[int])
for j := 0; j < bench_count; j++ {
tpl.Push(rand.Int())
}
@@ -24,7 +24,7 @@ func BenchmarkHeap_Push_Binary(b *testing.B) {
}
func BenchmarkHeap_Push_Quadratic(b *testing.B) {
- var tpl = heap.NewWithForks(heap.Quadratic, cmp.Less[int])
+ var tpl = heap.NewWithWays(heap.Quadratic, cmp.Less[int])
for j := 0; j < bench_count; j++ {
tpl.Push(rand.Int())
}
@@ -39,7 +39,7 @@ func BenchmarkHeap_Push_Quadratic(b *testing.B) {
}
}
func BenchmarkHeap_Push_Octal(b *testing.B) {
- var tpl = heap.NewWithForks(heap.Octal, cmp.Less[int])
+ var tpl = heap.NewWithWays(heap.Octal, cmp.Less[int])
for j := 0; j < bench_count; j++ {
tpl.Push(rand.Int())
}
@@ -55,7 +55,7 @@ func BenchmarkHeap_Push_Octal(b *testing.B) {
}
func BenchmarkHeap_Pop_Binary(b *testing.B) {
- var tpl = heap.NewWithForks(heap.Binary, cmp.Less[int])
+ var tpl = heap.NewWithWays(heap.Binary, cmp.Less[int])
for j := 0; j < bench_count*2; j++ {
tpl.Push(rand.Int())
}
@@ -70,7 +70,7 @@ func BenchmarkHeap_Pop_Binary(b *testing.B) {
}
func BenchmarkHeap_Pop_Quadratic(b *testing.B) {
- var tpl = heap.NewWithForks(heap.Quadratic, cmp.Less[int])
+ var tpl = heap.NewWithWays(heap.Quadratic, cmp.Less[int])
for j := 0; j < bench_count*2; j++ {
tpl.Push(rand.Int())
}
@@ -85,7 +85,7 @@ func BenchmarkHeap_Pop_Quadratic(b *testing.B) {
}
func BenchmarkHeap_Pop_Octal(b *testing.B) {
- var tpl = heap.NewWithForks(heap.Octal, cmp.Less[int])
+ var tpl = heap.NewWithWays(heap.Octal, cmp.Less[int])
for j := 0; j < bench_count*2; j++ {
tpl.Push(rand.Int())
}
diff --git a/hashmap/hashmap.go b/hashmap/hashmap.go
index af56003..b4722d4 100644
--- a/hashmap/hashmap.go
+++ b/hashmap/hashmap.go
@@ -33,6 +33,12 @@ func (c HashMap[K, V]) Get(key K) (val V, exist bool) {
return
}
+// Exists if key exists, return true
+func (c HashMap[K, V]) Exists(key K) bool {
+ _, ok := c[key]
+ return ok
+}
+
// Delete delete a element if the key exists
func (c HashMap[K, V]) Delete(key K) {
delete(c, key)
diff --git a/hashmap/hashmap_test.go b/hashmap/hashmap_test.go
index 471db56..d3fcaee 100644
--- a/hashmap/hashmap_test.go
+++ b/hashmap/hashmap_test.go
@@ -103,6 +103,8 @@ func TestHashMap_Range(t *testing.T) {
m.Set("a", 1)
m.Set("b", 2)
m.Set("c", 3)
+ assert.True(t, m.Exists("a"))
+ assert.False(t, m.Exists("d"))
var keys []string
m.Range(func(key string, val int) bool {
diff --git a/heap/heap.go b/heap/heap.go
index d732c08..e93d747 100644
--- a/heap/heap.go
+++ b/heap/heap.go
@@ -1,6 +1,8 @@
package heap
import (
+ "github.com/lxzan/dao/algorithm"
+ "github.com/lxzan/dao/internal/utils"
"github.com/lxzan/dao/types/cmp"
)
@@ -14,20 +16,20 @@ const (
// New 新建一个最小四叉堆
// Create a new minimum quadratic heap
-func New[T cmp.Ordered]() *Heap[T] { return NewWithForks(Quadratic, cmp.Less[T]) }
+func New[T cmp.Ordered]() *Heap[T] { return NewWithWays(Quadratic, cmp.Less[T]) }
-// NewWithForks 新建堆
-// @forks 分叉数, 可选值为: 2,4,6
+// NewWithWays 新建堆
+// @ways 分叉数, ways=pow(2,n)
// @lessFunc 比较函数
-func NewWithForks[T any](forks uint32, lessFunc cmp.LessFunc[T]) *Heap[T] {
+func NewWithWays[T any](ways uint32, lessFunc cmp.LessFunc[T]) *Heap[T] {
h := &Heap[T]{lessFunc: lessFunc}
- h.setForkNumber(forks)
+ h.setWays(ways)
return h
}
type Heap[T any] struct {
- bits uint32
- forks int
+ bits int
+ ways int
data []T
lessFunc func(a, b T) bool
}
@@ -38,17 +40,14 @@ func (c *Heap[T]) SetCap(n int) *Heap[T] {
return c
}
-// setForkNumber 设置分叉数
-func (c *Heap[T]) setForkNumber(n uint32) *Heap[T] {
- c.forks = int(n)
- switch n {
- case Quadratic, Binary:
- c.bits = n / 2
- case Octal:
- c.bits = 3
- default:
- panic("incorrect number of forks")
+// setWays 设置分叉数
+func (c *Heap[T]) setWays(n uint32) *Heap[T] {
+ n = algorithm.SelectValue(n == 0, Quadratic, n)
+ if !utils.IsBinaryNumber(n) {
+ panic("incorrect number of ways")
}
+ c.ways = int(n)
+ c.bits = utils.GetBinaryExponential(c.ways)
return c
}
@@ -66,29 +65,39 @@ func (c *Heap[T]) swap(i, j int) {
}
func (c *Heap[T]) up(i int) {
- var j = (i - 1) >> c.bits
- if i >= 1 && c.less(i, j) {
+ for i > 0 {
+ var j = (i - 1) >> c.bits
+ if !c.less(i, j) {
+ return
+ }
+
c.swap(i, j)
- c.up(j)
+ i = j
}
}
-func (c *Heap[T]) down(i, n int) {
- var base = i << c.bits
- var index = base + 1
- if index >= n {
- return
- }
+func (c *Heap[T]) down(i int) {
+ var n = c.Len()
+ for {
+ var base = i << c.bits
+ var index = base + 1
+ if index >= n {
+ return
+ }
- for j := base + 2; j <= base+c.forks && j < n; j++ {
- if c.less(j, index) {
- index = j
+ var end = algorithm.Min(base+c.ways, n-1)
+ for j := base + 2; j <= end; j++ {
+ if c.less(j, index) {
+ index = j
+ }
+ }
+
+ if !c.less(index, i) {
+ return
}
- }
- if c.less(index, i) {
c.swap(i, index)
- c.down(index, n)
+ i = index
}
}
@@ -115,7 +124,7 @@ func (c *Heap[T]) Pop() (ele T) {
ele = c.data[0]
c.data[0] = c.data[n-1]
c.data = c.data[:n-1]
- c.down(0, n-1)
+ c.down(0)
}
return
}
@@ -136,8 +145,7 @@ func (c *Heap[T]) Range(f func(index int, value T) bool) {
func (c *Heap[T]) Clone() *Heap[T] {
var v = *c
- v.data = make([]T, len(c.data))
- copy(v.data, c.data)
+ v.data = utils.Clone(c.data)
return &v
}
diff --git a/heap/heap_test.go b/heap/heap_test.go
index f5e4344..294e360 100644
--- a/heap/heap_test.go
+++ b/heap/heap_test.go
@@ -36,7 +36,7 @@ func TestNew(t *testing.T) {
}
func TestDesc(t *testing.T) {
- var h = NewWithForks(Octal, desc[int])
+ var h = NewWithWays(Octal, desc[int])
h.SetCap(8)
h.Push(1)
assert.Equal(t, h.Top(), 1)
@@ -53,7 +53,7 @@ func TestDesc(t *testing.T) {
}
func TestAsc(t *testing.T) {
- var h = NewWithForks(Binary, cmp.Less[int])
+ var h = NewWithWays(Binary, cmp.Less[int])
h.SetCap(8)
h.Push(1)
h.Push(3)
@@ -69,7 +69,7 @@ func TestAsc(t *testing.T) {
}
func TestHeap_Range(t *testing.T) {
- var h = NewWithForks(Quadratic, cmp.Less[int])
+ var h = NewWithWays(Quadratic, cmp.Less[int])
h.SetCap(8)
h.Push(1)
h.Push(3)
@@ -126,12 +126,12 @@ func TestHeap_SetForkNumber(t *testing.T) {
}
var err1 = catch(func() {
- NewWithForks(3, cmp.Less[int])
+ NewWithWays(3, cmp.Less[int])
})
assert.Error(t, err1)
var err2 = catch(func() {
- NewWithForks(4, cmp.Less[int])
+ NewWithWays(4, cmp.Less[int])
})
assert.Nil(t, err2)
}
@@ -154,7 +154,7 @@ func TestHeap_Clone(t *testing.T) {
}
func TestHeap_UnWrap(t *testing.T) {
- var h = NewWithForks(2, cmp.Less[int])
+ var h = NewWithWays(2, cmp.Less[int])
h.Push(1)
h.Push(2)
h.Push(3)
diff --git a/heap/index.go b/heap/index.go
new file mode 100644
index 0000000..a7c8100
--- /dev/null
+++ b/heap/index.go
@@ -0,0 +1,210 @@
+package heap
+
+import (
+ "github.com/lxzan/dao/algorithm"
+ "github.com/lxzan/dao/internal/utils"
+ "github.com/lxzan/dao/types/cmp"
+)
+
+type (
+ Element[K cmp.Ordered, V any] struct {
+ index int
+ key K
+ Value V
+ }
+
+ IndexedHeap[K cmp.Ordered, V any] struct {
+ bits int
+ ways int
+ data []*Element[K, V]
+ lessFunc func(a, b K) bool
+ }
+)
+
+// Key 获取排序Key
+func (c *Element[K, V]) Key() K {
+ return c.key
+}
+
+// Index 获取索引
+// index==-1 表示元素已被删除, 不允许做更新或删除操作
+func (c *Element[K, V]) Index() int {
+ return c.index
+}
+
+func (c *Element[K, V]) delete() {
+ c.index = -1
+}
+
+// NewIndexedHeap 新建索引堆
+// @ways 分叉数, ways=pow(2,n)
+// @lessFunc 比较函数, 可以传空指针, 默认为最小堆
+func NewIndexedHeap[K cmp.Ordered, V any](ways uint32, lessFunc cmp.LessFunc[K]) *IndexedHeap[K, V] {
+ var c = new(IndexedHeap[K, V])
+ c.setWays(ways)
+ c.lessFunc = lessFunc
+ if c.lessFunc == nil {
+ c.lessFunc = cmp.Less[K]
+ }
+ return c
+}
+
+// SetForkNumber 设置分叉数
+func (c *IndexedHeap[K, V]) setWays(n uint32) *IndexedHeap[K, V] {
+ n = algorithm.SelectValue(n == 0, Quadratic, n)
+ if !utils.IsBinaryNumber(n) {
+ panic("incorrect number of ways")
+ }
+ c.ways = int(n)
+ c.bits = utils.GetBinaryExponential(c.ways)
+ return c
+}
+
+// Len 获取元素数量
+func (c *IndexedHeap[K, V]) Len() int {
+ return len(c.data)
+}
+
+// Reset 重置堆
+func (c *IndexedHeap[K, V]) Reset() {
+ c.data = c.data[:0]
+}
+
+// SetCap 设置预分配容量
+func (c *IndexedHeap[K, V]) SetCap(n int) *IndexedHeap[K, V] {
+ c.data = make([]*Element[K, V], 0, n)
+ return c
+}
+
+func (c *IndexedHeap[K, V]) swap(i, j int) {
+ c.data[i].index, c.data[j].index = c.data[j].index, c.data[i].index
+ c.data[i], c.data[j] = c.data[j], c.data[i]
+}
+
+func (c *IndexedHeap[K, V]) less(i, j int) bool {
+ return c.lessFunc(c.data[i].key, c.data[j].key)
+}
+
+func (c *IndexedHeap[K, V]) up(i int) {
+ for i > 0 {
+ var j = (i - 1) >> c.bits
+ if !c.less(i, j) {
+ return
+ }
+
+ c.swap(i, j)
+ i = j
+ }
+}
+
+func (c *IndexedHeap[K, V]) down(i int) {
+ var n = c.Len()
+ for {
+ var base = i << c.bits
+ var index = base + 1
+ if index >= n {
+ return
+ }
+
+ var end = algorithm.Min(base+c.ways, n-1)
+ for j := base + 2; j <= end; j++ {
+ if c.less(j, index) {
+ index = j
+ }
+ }
+
+ if !c.less(index, i) {
+ return
+ }
+
+ c.swap(i, index)
+ i = index
+ }
+}
+
+// Push 追加元素
+func (c *IndexedHeap[K, V]) Push(key K, value V) *Element[K, V] {
+ ele := &Element[K, V]{key: key, Value: value}
+ ele.index = c.Len()
+ c.data = append(c.data, ele)
+ c.up(c.Len() - 1)
+ return ele
+}
+
+// Pop 弹出堆顶元素
+func (c *IndexedHeap[K, V]) Pop() (ele *Element[K, V]) {
+ var n = c.Len()
+ switch n {
+ case 0:
+ return ele
+ case 1:
+ ele = c.data[0]
+ c.data = c.data[:0]
+ default:
+ ele = c.data[0]
+ c.swap(0, n-1)
+ c.data = c.data[:n-1]
+ c.down(0)
+ }
+ ele.delete()
+ return ele
+}
+
+// UpdateKeyByIndex 通过索引更新排序Key
+func (c *IndexedHeap[K, V]) UpdateKeyByIndex(index int, key K) {
+ ele := c.data[index]
+ var down = c.lessFunc(ele.key, key)
+ ele.key = key
+ if down {
+ c.down(ele.index)
+ } else {
+ c.up(ele.index)
+ }
+}
+
+// GetByIndex 通过索引获取元素
+func (c *IndexedHeap[K, V]) GetByIndex(index int) *Element[K, V] {
+ return c.data[index]
+}
+
+// DeleteByIndex 通过索引删除元素
+func (c *IndexedHeap[K, V]) DeleteByIndex(index int) {
+ if index == 0 {
+ c.Pop()
+ return
+ }
+
+ var n = c.Len()
+ var down = c.less(index, n-1)
+ c.swap(index, n-1)
+ c.data[n-1].delete()
+ c.data = c.data[:n-1]
+ if index < n-1 {
+ if down {
+ c.down(index)
+ } else {
+ c.up(index)
+ }
+ }
+}
+
+// Top 获取堆顶元素
+func (c *IndexedHeap[K, V]) Top() *Element[K, V] {
+ return c.data[0]
+}
+
+// Range 遍历
+func (c *IndexedHeap[K, V]) Range(f func(ele *Element[K, V]) bool) {
+ for _, v := range c.data {
+ if !f(v) {
+ return
+ }
+ }
+}
+
+// Clone 拷贝索引堆副本
+func (c *IndexedHeap[K, V]) Clone() *IndexedHeap[K, V] {
+ var v = *c
+ v.data = utils.Clone(c.data)
+ return &v
+}
diff --git a/heap/index_test.go b/heap/index_test.go
new file mode 100644
index 0000000..024833b
--- /dev/null
+++ b/heap/index_test.go
@@ -0,0 +1,226 @@
+package heap
+
+import (
+ "fmt"
+ "github.com/lxzan/dao/algorithm"
+ "github.com/lxzan/dao/internal/utils"
+ "github.com/lxzan/dao/types/cmp"
+ "github.com/stretchr/testify/assert"
+ "math/rand"
+ "testing"
+ "unsafe"
+)
+
+func TestIndexedHeap_Sort(t *testing.T) {
+ var h = NewIndexedHeap[int, struct{}](Quadratic, nil)
+ for i := 0; i < 1000; i++ {
+ h.Push(rand.Int(), struct{}{})
+ }
+ var arr []int
+ for h.Len() > 0 {
+ arr = append(arr, h.Pop().Key())
+ }
+ assert.True(t, algorithm.IsSorted(arr, func(a, b int) int {
+ if a > b {
+ return 1
+ } else if a < b {
+ return -1
+ } else {
+ return 0
+ }
+ }))
+}
+
+func TestHeap_Random(t *testing.T) {
+ t.Run("asc", func(t *testing.T) {
+ const count = 10000
+ var h = NewIndexedHeap[int, struct{}](Quadratic, cmp.Less[int])
+ h.SetCap(count)
+ for i := 0; i < count; i++ {
+ flag := rand.Intn(5)
+ key := rand.Intn(count)
+ switch flag {
+ case 0, 1:
+ h.Push(key, struct{}{})
+ case 2:
+ h.Pop()
+ case 3:
+ n := h.Len()
+ if n > 0 {
+ index := rand.Intn(n)
+ h.UpdateKeyByIndex(index, key)
+ }
+ case 4:
+ n := h.Len()
+ if n > 0 {
+ index := rand.Intn(n)
+ h.DeleteByIndex(index)
+ }
+ }
+ }
+
+ for i, item := range h.data {
+ assert.Equal(t, item.Index(), i)
+
+ if item.Index() == 0 {
+ item = h.Top()
+ }
+ var n = h.Len()
+ var base = i << h.bits
+ var end = algorithm.Min(base+h.ways, n-1)
+ for j := base + 1; j <= end; j++ {
+ assert.True(t, h.lessFunc(item.Key(), h.GetByIndex(j).Key()))
+ }
+ }
+ })
+
+ t.Run("desc", func(t *testing.T) {
+ const count = 10000
+ var h = NewIndexedHeap[int, struct{}](Quadratic, func(a, b int) bool {
+ return a > b
+ })
+ h.SetCap(count)
+ for i := 0; i < count; i++ {
+ flag := rand.Intn(5)
+ key := rand.Intn(count)
+ switch flag {
+ case 0, 1:
+ h.Push(key, struct{}{})
+ case 2:
+ h.Pop()
+ case 3:
+ n := h.Len()
+ if n > 0 {
+ index := rand.Intn(n)
+ h.UpdateKeyByIndex(index, key)
+ }
+ case 4:
+ n := h.Len()
+ if n > 0 {
+ index := rand.Intn(n)
+ h.DeleteByIndex(index)
+ }
+ }
+ }
+
+ for i, item := range h.data {
+ assert.Equal(t, item.Index(), i)
+
+ if item.Index() == 0 {
+ item = h.Top()
+ }
+ var n = h.Len()
+ var base = i << h.bits
+ var end = algorithm.Min(base+h.ways, n-1)
+ for j := base + 1; j <= end; j++ {
+ assert.True(t, h.lessFunc(item.Key(), h.GetByIndex(j).Key()))
+ }
+ }
+ })
+}
+
+func TestIndexedHeap_Range(t *testing.T) {
+ var h = NewIndexedHeap[int, struct{}](Quadratic, cmp.Less[int])
+ h.SetCap(8)
+ h.Push(1, struct{}{})
+ h.Push(3, struct{}{})
+ h.Push(2, struct{}{})
+ h.Push(5, struct{}{})
+ h.Push(4, struct{}{})
+
+ {
+ var arr []int
+ h.Range(func(ele *Element[int, struct{}]) bool {
+ arr = append(arr, ele.Key())
+ return true
+ })
+ assert.ElementsMatch(t, arr, []int{1, 2, 3, 4, 5})
+ }
+
+ {
+ var arr []int
+ h.Range(func(ele *Element[int, struct{}]) bool {
+ arr = append(arr, ele.Key())
+ return len(arr) < 2
+ })
+ assert.Equal(t, len(arr), 2)
+ }
+}
+
+func TestIndexedHeap_SetForkNumber(t *testing.T) {
+ var catch = func(f func()) (err error) {
+ defer func() {
+ if excp := recover(); excp != nil {
+ err = fmt.Errorf("%v", excp)
+ }
+ }()
+ f()
+ return err
+ }
+
+ var err1 = catch(func() {
+ NewIndexedHeap[int, struct{}](3, cmp.Less[int])
+ })
+ assert.Error(t, err1)
+
+ var err2 = catch(func() {
+ NewWithWays(4, cmp.Less[int])
+ })
+ assert.Nil(t, err2)
+}
+
+func TestIndexedHeap_Clone(t *testing.T) {
+ var h = NewIndexedHeap[int, struct{}](4, nil)
+ h.Push(1, struct{}{})
+ h.Push(3, struct{}{})
+ h.Push(4, struct{}{})
+ h.Push(4, struct{}{})
+
+ var h1 = h.Clone()
+ var h2 = h
+ assert.True(t, utils.IsSameSlice(h.data, h1.data))
+ var addr = (uintptr)(unsafe.Pointer(&h.data[0]))
+ var addr1 = (uintptr)(unsafe.Pointer(&h1.data[0]))
+ var addr2 = (uintptr)(unsafe.Pointer(&h2.data[0]))
+ assert.NotEqual(t, addr, addr1)
+ assert.Equal(t, addr, addr2)
+
+ h1.Reset()
+ assert.Equal(t, h1.Len(), 0)
+ assert.NotEqual(t, h2.Len(), 0)
+}
+
+func TestIndexedHeap_DeleteByIndex(t *testing.T) {
+ t.Run("", func(t *testing.T) {
+ var h = NewIndexedHeap[int, string](Quadratic, cmp.Less[int])
+ h.Push(1, "")
+ h.Push(2, "")
+ var ele = h.Push(3, "")
+ h.Push(4, "")
+ h.DeleteByIndex(ele.Index())
+ assert.Equal(t, ele.Index(), -1)
+ var arr []int
+ h.Range(func(ele *Element[int, string]) bool {
+ arr = append(arr, ele.Key())
+ return true
+ })
+ assert.ElementsMatch(t, arr, []int{1, 2, 4})
+ })
+
+ t.Run("", func(t *testing.T) {
+ var h = NewIndexedHeap[int, string](Quadratic, cmp.Less[int])
+ h.Push(1, "")
+ h.Push(2, "")
+ h.Push(3, "")
+ h.Push(4, "")
+ var ele = h.Pop()
+ assert.Equal(t, ele.Index(), -1)
+
+ var arr []int
+ h.Range(func(ele *Element[int, string]) bool {
+ arr = append(arr, ele.Key())
+ return true
+ })
+ assert.ElementsMatch(t, arr, []int{2, 3, 4})
+ })
+}
diff --git a/internal/utils/helper.go b/internal/utils/helper.go
index 3795c59..f89f0b8 100644
--- a/internal/utils/helper.go
+++ b/internal/utils/helper.go
@@ -64,6 +64,16 @@ func IsBinaryNumber[T Integer](x T) bool {
return x&(x-1) == 0
}
+// GetBinaryExponential 获取指数
+func GetBinaryExponential(n int) int {
+ sum := 0
+ for n > 1 {
+ n >>= 1
+ sum++
+ }
+ return sum
+}
+
func Clone[S ~[]E, E any](s S) S {
if s == nil {
return nil
diff --git a/internal/utils/helper_test.go b/internal/utils/helper_test.go
index 0aa6490..96d47fd 100644
--- a/internal/utils/helper_test.go
+++ b/internal/utils/helper_test.go
@@ -54,6 +54,7 @@ func TestReverseStrings(t *testing.T) {
}
func TestIsBinaryNumber(t *testing.T) {
+ assert.True(t, IsBinaryNumber(0))
assert.True(t, IsBinaryNumber(1))
assert.True(t, IsBinaryNumber(2))
assert.True(t, IsBinaryNumber(16))
@@ -76,3 +77,12 @@ func TestClone(t *testing.T) {
assert.ElementsMatch(t, b, a)
}
}
+
+func TestGetBinaryExponential(t *testing.T) {
+ assert.Equal(t, GetBinaryExponential(1), 0)
+ assert.Equal(t, GetBinaryExponential(2), 1)
+ assert.Equal(t, GetBinaryExponential(4), 2)
+ assert.Equal(t, GetBinaryExponential(8), 3)
+ assert.Equal(t, GetBinaryExponential(512), 9)
+ assert.Equal(t, GetBinaryExponential(1024), 10)
+}