diff --git a/README.md b/README.md index cb7c2cf..fbb0f86 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ data containers and algorithms to simplify business development. - [Filter](#filter) - [Heap](#heap) - [N-Way Heap](#n-way-heap) - - [N-Way Indexed Heap](#n-way-indexed-heap) + - [HashHeap](#hashheap) - [Stack](#stack) - [Queue](#queue) - [Deque](#deque) @@ -128,9 +128,9 @@ func main() { ``` -#### N-Way Indexed Heap +#### HashHeap -Extends the normal heap with update and delete functions, often used in time heap algorithms. +Heap structure with hash index. ```go package main @@ -140,17 +140,20 @@ import ( ) 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) + var h = heap.NewHashHeap[string, int](func(a, b int) bool { return a < b }) + h.Set("a", 1) + h.Set("b", 2) + h.Set("c", 3) + h.Set("d", 4) + h.Set("e", 5) + h.Set("f", 6) + + h.Delete("c") + h.Set("d", 0) + h.Set("g", 3) for h.Len() > 0 { - println(h.Pop().Key()) + ele := h.Pop() + println(ele.Key(), ele.Value()) } } diff --git a/README_CN.md b/README_CN.md index b34a4f6..979a57b 100644 --- a/README_CN.md +++ b/README_CN.md @@ -19,12 +19,12 @@ - [简介](#简介) - [目录](#目录) - [动态数组](#动态数组) - - [去重](#去重) - - [排序](#排序) - - [过滤](#过滤) + - [去重](#去重) + - [排序](#排序) + - [过滤](#过滤) - [堆](#堆) - - [N叉堆](#n叉堆) - - [N叉索引堆](#n叉索引堆) + - [N叉堆](#n叉堆) + - [哈希堆](#哈希堆) - [栈](#栈) - [队列](#队列) - [双端队列](#双端队列) @@ -123,9 +123,9 @@ func main() { ``` -#### N叉索引堆 +#### 哈希堆 -在普通堆的基础上拓展了更新和删除功能, 常用于时间堆算法. +带哈希索引的堆结构. ```go package main @@ -135,17 +135,20 @@ import ( ) 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) + var h = heap.NewHashHeap[string, int](func(a, b int) bool { return a < b }) + h.Set("a", 1) + h.Set("b", 2) + h.Set("c", 3) + h.Set("d", 4) + h.Set("e", 5) + h.Set("f", 6) + + h.Delete("c") + h.Set("d", 0) + h.Set("g", 3) for h.Len() > 0 { - println(h.Pop().Key()) + ele := h.Pop() + println(ele.Key(), ele.Value()) } } diff --git a/algo/algorithm.go b/algo/algorithm.go index 3d3ad1b..a2b984e 100644 --- a/algo/algorithm.go +++ b/algo/algorithm.go @@ -4,6 +4,7 @@ import ( "github.com/lxzan/dao/types/cmp" "reflect" "strconv" + "strings" ) // ToString 数字转字符串 @@ -135,13 +136,14 @@ func Map[A any, B any](arr []A, transfer func(i int, v A) B) []B { // Filter 过滤器 func Filter[T any, A ~[]T](arr A, check func(i int, v T) bool) A { - var results = make([]T, 0, len(arr)) + var j = 0 for i, v := range arr { if check(i, v) { - results = append(results, arr[i]) + arr[j] = arr[i] + j++ } } - return results + return arr[:j] } // IsZero 零值判断 @@ -171,7 +173,7 @@ func NotNil(v any) bool { } // GroupBy 分组 -func GroupBy[T any, A ~[]T, K cmp.Ordered](arr A, transfer func(i int, v T) K) map[K]A { +func GroupBy[T any, K cmp.Ordered, A ~[]T](arr A, transfer func(i int, v T) K) map[K]A { var m = make(map[K]A, len(arr)) for index, value := range arr { key := transfer(index, value) @@ -179,3 +181,18 @@ func GroupBy[T any, A ~[]T, K cmp.Ordered](arr A, transfer func(i int, v T) K) m } return m } + +func SplitWithCallback(s string, sep string, cb func(index int, item string) bool) { + var n = len(sep) + var index = 0 + for i := strings.Index(s, sep); i != -1; i = strings.Index(s, sep) { + if !cb(index, s[:i]) { + return + } + index++ + if i+n <= len(s) { + s = s[i+n:] + } + } + cb(index, s) +} diff --git a/algo/algorithm_test.go b/algo/algorithm_test.go index 55add91..57dbde6 100644 --- a/algo/algorithm_test.go +++ b/algo/algorithm_test.go @@ -171,11 +171,23 @@ func TestContains(t *testing.T) { } func TestFilter(t *testing.T) { - var arr = []int{1, 2, 3, 4} - arr = Filter(arr, func(i int, item int) bool { - return item%2 == 0 + t.Run("", func(t *testing.T) { + var arr = []int{1, 2, 3, 4} + arr = Filter(arr, func(i int, item int) bool { return item%2 == 0 }) + assert.ElementsMatch(t, arr, []int{2, 4}) + }) + + t.Run("", func(t *testing.T) { + var arr = []int{1, 2, 3, 4} + arr = Filter(arr, func(i int, item int) bool { return item%2 == 1 }) + assert.ElementsMatch(t, arr, []int{1, 3}) + }) + + t.Run("", func(t *testing.T) { + var arr = []int{} + arr = Filter(arr, func(i int, item int) bool { return item%2 == 0 }) + assert.ElementsMatch(t, arr, []int{}) }) - assert.ElementsMatch(t, arr, []int{2, 4}) } func TestIsZero(t *testing.T) { @@ -240,6 +252,10 @@ func TestIsNil(t *testing.T) { assert.True(t, IsNil(conn1)) assert.True(t, IsNil(conn2)) assert.False(t, IsNil(conn3)) + + assert.False(t, NotNil(conn1)) + assert.False(t, NotNil(conn2)) + assert.True(t, NotNil(conn3)) } func TestNotNil(t *testing.T) { @@ -253,3 +269,35 @@ func TestWithDefault(t *testing.T) { assert.Equal(t, WithDefault("", "1"), "1") assert.Equal(t, WithDefault("2", "1"), "2") } + +func TestSplitWithCallback(t *testing.T) { + t.Run("", func(t *testing.T) { + var path = "/api/v1/greet" + var values []string + SplitWithCallback(path, "/", func(index int, item string) bool { + values = append(values, item) + return true + }) + assert.ElementsMatch(t, values, []string{"", "api", "v1", "greet"}) + }) + + t.Run("", func(t *testing.T) { + var path = "/api/v1/greet/" + var values []string + SplitWithCallback(path, "/", func(index int, item string) bool { + values = append(values, item) + return true + }) + assert.ElementsMatch(t, values, []string{"", "api", "v1", "greet", ""}) + }) + + t.Run("", func(t *testing.T) { + var path = "/api/v1/greet" + var values []string + SplitWithCallback(path, "/", func(index int, item string) bool { + values = append(values, item) + return len(values) < 2 + }) + assert.ElementsMatch(t, values, []string{"", "api"}) + }) +} diff --git a/heap/hash.go b/heap/hash.go new file mode 100644 index 0000000..9556dca --- /dev/null +++ b/heap/hash.go @@ -0,0 +1,182 @@ +package heap + +import ( + "github.com/lxzan/dao/algo" + "github.com/lxzan/dao/types/cmp" +) + +type ( + Element[K comparable, V any] struct { + index int + key K + value V + } + + // HashHeap 带哈希索引的四叉堆, 简称哈希堆 + HashHeap[K comparable, V any] struct { + heap *underlyingHeap[K, V] + mapping map[K]*Element[K, V] + } +) + +func (c *Element[K, V]) Key() K { return c.key } + +func (c *Element[K, V]) Value() V { return c.value } + +// NewHashHeap 创建一个哈希堆 +func NewHashHeap[K comparable, V any](less cmp.LessFunc[V]) *HashHeap[K, V] { + return &HashHeap[K, V]{ + heap: &underlyingHeap[K, V]{lessFunc: less}, + mapping: make(map[K]*Element[K, V]), + } +} + +// Len 返回元素数量 +func (c *HashHeap[K, V]) Len() int { return len(c.mapping) } + +// Set 设置键值 +func (c *HashHeap[K, V]) Set(k K, v V) { + ele, ok := c.mapping[k] + if !ok { + ele = &Element[K, V]{key: k, value: v} + c.mapping[k] = ele + c.heap.Push(ele) + return + } + c.heap.Update(ele, v) +} + +// Get 查询 +func (c *HashHeap[K, V]) Get(k K) (V, bool) { + v, ok := c.mapping[k] + return v.value, ok +} + +// Delete 删除一个元素 +func (c *HashHeap[K, V]) Delete(k K) { + ele, ok := c.mapping[k] + if !ok { + return + } + c.heap.Delete(ele.index) + delete(c.mapping, k) +} + +// Top 返回堆顶元素 +func (c *HashHeap[K, V]) Top() *Element[K, V] { + if c.Len() > 0 { + return c.heap.data[0] + } + return nil +} + +// Pop 弹出堆顶元素 +func (c *HashHeap[K, V]) Pop() *Element[K, V] { + if ele := c.Top(); ele != nil { + c.Delete(ele.key) + return ele + } + return nil +} + +// Range 遍历堆元素 +func (c *HashHeap[K, V]) Range(f func(ele *Element[K, V]) bool) { + for _, ele := range c.heap.data { + if !f(ele) { + return + } + } +} + +type underlyingHeap[K comparable, V any] struct { + data []*Element[K, V] + lessFunc func(a, b V) bool +} + +func (c *underlyingHeap[K, V]) Len() int { + return len(c.data) +} + +func (c *underlyingHeap[K, V]) Push(ele *Element[K, V]) { + ele.index = c.Len() + c.data = append(c.data, ele) + c.up(c.Len() - 1) +} + +func (c *underlyingHeap[K, V]) up(i int) { + var j = (i - 1) >> 2 + if i >= 1 && c.lessFunc(c.data[i].value, c.data[j].value) { + c.swap(i, j) + c.up(j) + } +} + +func (c *underlyingHeap[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 *underlyingHeap[K, V]) Pop() (ele *Element[K, V]) { + var n = c.Len() + switch n { + case 0: + 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, n-1) + } + return +} + +func (c *underlyingHeap[K, V]) down(i, n int) { + var base = i << 2 + var index = base + 1 + if index >= n { + return + } + + var end = algo.Min(base+4, n-1) + for j := base + 2; j <= end; j++ { + if c.lessFunc(c.data[j].value, c.data[index].value) { + index = j + } + } + + if c.lessFunc(c.data[index].value, c.data[i].value) { + c.swap(i, index) + c.down(index, n) + } +} + +func (c *underlyingHeap[K, V]) Update(ele *Element[K, V], value V) { + var down = c.lessFunc(ele.value, value) + ele.value = value + if down { + c.down(ele.index, c.Len()) + } else { + c.up(ele.index) + } +} + +func (c *underlyingHeap[K, V]) Delete(i int) { + if i == 0 { + c.Pop() + return + } + + var n = c.Len() + var down = c.lessFunc(c.data[i].value, c.data[n-1].value) + c.swap(i, n-1) + c.data = c.data[:n-1] + if i < n-1 { + if down { + c.down(i, n-1) + } else { + c.up(i) + } + } +} diff --git a/heap/hash_test.go b/heap/hash_test.go new file mode 100644 index 0000000..31a7e69 --- /dev/null +++ b/heap/hash_test.go @@ -0,0 +1,96 @@ +package heap + +import ( + "github.com/lxzan/dao/algo" + "github.com/lxzan/dao/internal/utils" + "github.com/lxzan/dao/types/cmp" + "github.com/stretchr/testify/assert" + "math/rand" + "testing" +) + +func validateIndexedHeap[K cmp.Ordered, V cmp.Ordered](t *testing.T, h *HashHeap[K, V], m map[K]V, compare cmp.CompareFunc[V]) { + var n = h.Len() + if n > 0 { + assert.Equal(t, h.heap.data[0].Key(), h.Top().Key()) + } + + var i = 0 + h.Range(func(ele *Element[K, V]) bool { + assert.Equal(t, ele.index, i) + i++ + + var base = ele.index << 2 + var end = algo.Min(base+4, n-1) + for j := base + 1; j <= end; j++ { + child := h.heap.data[j] + assert.True(t, compare(ele.Value(), child.Value()) <= 0) + } + return true + }) + + assert.Equal(t, len(m), h.Len()) + for k, v := range m { + v2, _ := h.Get(k) + assert.Equal(t, v, v2) + } + + var values = make([]V, 0, n) + for h.Len() > 0 { + values = append(values, h.Pop().Value()) + } + assert.True(t, algo.IsSorted(values, compare)) +} + +func TestIndexedHeap_Random(t *testing.T) { + const count = 10000 + + var f = func(lessFunc cmp.LessFunc[int], compareFunc cmp.CompareFunc[int]) { + var h = NewHashHeap[int, int](lessFunc) + var m = make(map[int]int) + for i := 0; i < count; i++ { + flag := utils.Alphabet.Intn(6) + key := rand.Intn(count) + switch flag { + case 0, 1, 2: + h.Set(key, i) + m[key] = i + case 3: + if ele := h.Pop(); ele != nil { + delete(m, ele.Key()) + } + case 4, 5: + h.Delete(key) + delete(m, key) + } + } + + validateIndexedHeap(t, h, m, compareFunc) + } + + f(cmp.Less[int], cmp.Compare[int]) + f(cmp.Great[int], compareDesc[int]) +} + +func TestHashHeap_Range(t *testing.T) { + var hm = NewHashHeap[string, int](func(a, b int) bool { + return a < b + }) + hm.Set("a", 1) + hm.Set("b", 2) + hm.Set("c", 3) + var values []int + hm.Range(func(ele *Element[string, int]) bool { + values = append(values, ele.Value()) + return len(values) < 2 + }) + assert.Equal(t, len(values), 2) +} + +func TestHashHeap_Pop(t *testing.T) { + var hm = NewHashHeap[string, int](func(a, b int) bool { + return a < b + }) + assert.Nil(t, hm.Pop()) + assert.Nil(t, hm.heap.Pop()) +} diff --git a/heap/index.go b/heap/index.go deleted file mode 100644 index dd9e567..0000000 --- a/heap/index.go +++ /dev/null @@ -1,210 +0,0 @@ -package heap - -import ( - "github.com/lxzan/dao/algo" - "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 = algo.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 = algo.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 deleted file mode 100644 index 02c4179..0000000 --- a/heap/index_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package heap - -import ( - "fmt" - "github.com/lxzan/dao/algo" - "github.com/lxzan/dao/internal/utils" - "github.com/lxzan/dao/types/cmp" - "github.com/stretchr/testify/assert" - "math/rand" - "testing" - "unsafe" -) - -func validateIndexedHeap[K cmp.Ordered, V any](t *testing.T, h *IndexedHeap[K, V], compare cmp.CompareFunc[K]) { - var n = h.Len() - if n > 0 { - assert.Equal(t, h.data[0].Key(), h.Top().Key()) - } - - var i = 0 - h.Range(func(ele *Element[K, V]) bool { - assert.Equal(t, ele.Index(), i) - i++ - - var base = ele.Index() << h.bits - var end = algo.Min(base+h.ways, n-1) - for j := base + 1; j <= end; j++ { - child := h.GetByIndex(j) - assert.True(t, compare(ele.Key(), child.Key()) <= 0) - } - return true - }) - - var keys = make([]K, 0, n) - for h.Len() > 0 { - keys = append(keys, h.Pop().Key()) - } - assert.True(t, algo.IsSorted(keys, compare)) -} - -func TestIndexedHeap_Random(t *testing.T) { - const count = 10000 - - var f = func(ways uint32, lessFunc cmp.LessFunc[int], compareFunc cmp.CompareFunc[int]) { - var h = NewIndexedHeap[int, struct{}](ways, lessFunc) - h.SetCap(count) - for i := 0; i < count; i++ { - flag := utils.Alphabet.Intn(6) - key := rand.Intn(count) - switch flag { - case 0, 1, 2: - h.Push(key, struct{}{}) - case 3: - h.Pop() - case 4: - n := h.Len() - if n > 0 { - index := rand.Intn(n) - h.UpdateKeyByIndex(index, key) - } - case 5: - n := h.Len() - if n > 0 { - index := rand.Intn(n) - h.DeleteByIndex(index) - } - } - } - - validateIndexedHeap(t, h, compareFunc) - } - - f(2, cmp.Less[int], cmp.Compare[int]) - f(2, cmp.Great[int], compareDesc[int]) - f(4, cmp.Less[int], cmp.Compare[int]) - f(4, cmp.Great[int], compareDesc[int]) - f(8, cmp.Less[int], cmp.Compare[int]) - f(8, cmp.Great[int], compareDesc[int]) - f(16, cmp.Less[int], cmp.Compare[int]) - f(16, cmp.Great[int], compareDesc[int]) -} - -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, algo.IsSorted(arr, func(a, b int) int { - if a > b { - return 1 - } else if a < b { - return -1 - } else { - return 0 - } - })) -} - -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}) - }) -}