├── LICENSE ├── README.md ├── counters ├── bystr.go └── bystr_test.go ├── go.mod ├── go.sum └── heap ├── float64s.go ├── float64s_test.go ├── heap.go ├── heap_test.go ├── interfaces.go ├── ints.go ├── ints_test.go ├── strings.go └── strings_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Golang Plus 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of container nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # container 2 | Plus to the standard container packages 3 | 4 | ## Featured 5 | 6 | `heap` package is an alternative to builtin `container/heap` package with much simpler usage and obvious performance improvement. Details are introduced [here](http://daviddengcn.blogspot.com/2015/06/an-alternative-design-for-containerheap.html). 7 | -------------------------------------------------------------------------------- /counters/bystr.go: -------------------------------------------------------------------------------- 1 | // Package counters provide types supporting flexible counters within a map. 2 | // Since counters are mergable, all types are designed not to be thread safe. 3 | package counters 4 | 5 | // ByString is a map of counters with a string as the key. 6 | type ByString map[string]int 7 | 8 | // Add increases the value of a specific key and returns the updated value. 9 | func (bs *ByString) Add(key string, inc int) int { 10 | if *bs == nil { 11 | *bs = make(map[string]int) 12 | } 13 | v := (*bs)[key] + inc 14 | (*bs)[key] = v 15 | return v 16 | } 17 | 18 | // MergeWith adds values of another ByString counters into this one. 19 | func (bs *ByString) MergeWith(that ByString) { 20 | if len(that) == 0 { 21 | return 22 | } 23 | if *bs == nil { 24 | *bs = make(map[string]int) 25 | } 26 | for k, v := range that { 27 | (*bs)[k] = (*bs)[k] + v 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /counters/bystr_test.go: -------------------------------------------------------------------------------- 1 | package counters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golangplus/testing/assert" 7 | ) 8 | 9 | func TestByString(t *testing.T) { 10 | var c1 ByString 11 | assert.Equal(t, "c1", c1, ByString{}) 12 | 13 | c1.Add("one", 1) 14 | c1.Add("two", 2) 15 | assert.Equal(t, "c1", c1, ByString{"one": 1, "two": 2}) 16 | 17 | c1.MergeWith(nil) 18 | 19 | c1.MergeWith(ByString{"two": 2, "three": 3}) 20 | assert.Equal(t, "c1", c1, ByString{"one": 1, "two": 4, "three": 3}) 21 | 22 | c1 = nil 23 | c1.MergeWith(ByString{"two": 2, "three": 3}) 24 | assert.Equal(t, "c1", c1, ByString{"two": 2, "three": 3}) 25 | } 26 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/golangplus/container 2 | 3 | go 1.14 4 | 5 | require github.com/golangplus/testing v1.0.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450 h1:7xqw01UYS+KCI25bMrPxwNYkSns2Db1ziQPpVq99FpE= 2 | github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= 3 | github.com/golangplus/fmt v1.0.0 h1:FnUKtw86lXIPfBMc3FimNF3+ABcV+aH5F17OOitTN+E= 4 | github.com/golangplus/fmt v1.0.0/go.mod h1:zpM0OfbMCjPtd2qkTD/jX2MgiFCqklhSUFyDW44gVQE= 5 | github.com/golangplus/testing v1.0.0 h1:+ZeeiKZENNOMkTTELoSySazi+XaEhVO0mb+eanrSEUQ= 6 | github.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA= 7 | -------------------------------------------------------------------------------- /heap/float64s.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | // A heap for strings. The pointer to the zero value of Float64s is a heap with default less 8 | // func which compares string values on the natual order. 9 | // Use NewFloat64s to customize less func and initial capacity. 10 | type Float64s struct { 11 | less func(i, j int) bool 12 | list sort.Float64Slice 13 | } 14 | 15 | // Len returns the number of elements in the current heap. 16 | func (h *Float64s) Len() int { 17 | return len(h.list) 18 | } 19 | 20 | // Push inserts an element to the heap. 21 | func (h *Float64s) Push(x float64) { 22 | h.list = append(h.list, x) 23 | 24 | if h.less == nil { 25 | PushLastF(len(h.list), h.list.Less, h.list.Swap) 26 | } else { 27 | PushLastF(len(h.list), h.less, h.list.Swap) 28 | } 29 | } 30 | 31 | // Peek returns the top most element. It panics if the heap is empty. 32 | func (h *Float64s) Peek() float64 { 33 | return h.list[0] 34 | } 35 | 36 | // Pop removes the top element from the heap and returns it. 37 | func (h *Float64s) Pop() float64 { 38 | if h.less == nil { 39 | PopToLastF(len(h.list), h.list.Less, h.list.Swap) 40 | } else { 41 | PopToLastF(len(h.list), h.less, h.list.Swap) 42 | } 43 | 44 | res := h.list[len(h.list)-1] 45 | h.list = h.list[:len(h.list)-1] 46 | 47 | return res 48 | } 49 | 50 | // PopAll pops and returns all elements of the heap in reverse order. 51 | func (h *Float64s) PopAll() []float64 { 52 | for n := h.Len(); n > 1; n-- { 53 | if h.less == nil { 54 | PopToLastF(n, h.list.Less, h.list.Swap) 55 | } else { 56 | PopToLastF(n, h.less, h.list.Swap) 57 | } 58 | } 59 | res := h.list 60 | h.list = nil 61 | return res 62 | } 63 | 64 | // NewFloat64s returns a *Float64s with customized less func and initial capacity. 65 | func NewFloat64s(less func(x, y float64) bool, cap int) *Float64s { 66 | h := &Float64s{} 67 | 68 | if less != nil { 69 | h.less = func(i, j int) bool { 70 | return less(h.list[i], h.list[j]) 71 | } 72 | } 73 | if cap > 0 { 74 | h.list = make([]float64, 0, cap) 75 | } 76 | 77 | return h 78 | } 79 | -------------------------------------------------------------------------------- /heap/float64s_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golangplus/testing/assert" 7 | ) 8 | 9 | func TestFloat64s_DefLess(t *testing.T) { 10 | var h Float64s 11 | 12 | assert.Equal(t, "len", h.Len(), 0) 13 | 14 | h.Push(5) 15 | h.Push(2) 16 | h.Push(1) 17 | h.Push(3) 18 | 19 | assert.Equal(t, "len", h.Len(), 4) 20 | assert.Equal(t, "peek", h.Peek(), 1.) 21 | 22 | res := []float64{h.Pop(), h.Pop(), h.Pop(), h.Pop()} 23 | assert.Equal(t, "res", res, []float64{1, 2, 3, 5}) 24 | 25 | h.Push(5) 26 | h.Push(2) 27 | h.Push(1) 28 | h.Push(3) 29 | assert.Equal(t, "PopAll", h.PopAll(), []float64{5, 3, 2, 1}) 30 | } 31 | 32 | func TestFloat64s_CustomLess(t *testing.T) { 33 | h := NewFloat64s(func(x, y float64) bool { 34 | return x > y 35 | }, 5) 36 | 37 | assert.Equal(t, "len", h.Len(), 0) 38 | 39 | h.Push(5) 40 | h.Push(2) 41 | h.Push(1) 42 | h.Push(3) 43 | 44 | assert.Equal(t, "len", h.Len(), 4) 45 | assert.Equal(t, "peek", h.Peek(), 5.) 46 | res := []float64{h.Pop(), h.Pop(), h.Pop(), h.Pop()} 47 | assert.StringEqual(t, "res", res, []float64{5, 3, 2, 1}) 48 | 49 | h.Push(5) 50 | h.Push(2) 51 | h.Push(1) 52 | h.Push(3) 53 | assert.Equal(t, "PopAll", h.PopAll(), []float64{1, 2, 3, 5}) 54 | } 55 | -------------------------------------------------------------------------------- /heap/heap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Golang Plus Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package heap is an alternative to the standard "heap" package. 7 | It implements a very similar function to the builtin 8 | heap(priority queue) package except the elements are not necessarily 9 | interface{}, but can be of any type. 10 | 11 | The trick is to use the last element as the in/out place. Push/Pop/Remove are 12 | replaced with PushLast/PopToLast/RemoveToLast, respectively. 13 | 14 | A heap with int value can be easily implemented as follow: 15 | 16 | type IntHeap []int 17 | func (h *IntHeap) Pop() int { 18 | heap.PopToLast((*sort.IntSlice)(h)) 19 | res := (*h)[len(*h) - 1] 20 | *h = (*h)[:len(*h) - 1] 21 | 22 | return res 23 | } 24 | 25 | func (h *IntHeap) Push(x int) { 26 | *h = append(*h, x) 27 | heap.PushLast((*sort.IntSlice)(h)) 28 | } 29 | 30 | Use of the IntHeap: 31 | 32 | hp := IntHeap{3, 1, 5} 33 | heap.Init(sort.Interface(h)) 34 | hp.Push(4) 35 | ... 36 | value := hp.Pop() 37 | 38 | PushLastF/PopToLastF/RemoveToLastF takes funcs other than sort.Interface as the argument. 39 | E.g., a heap with type T value can be implemented as follow: 40 | 41 | type THeap []T 42 | func (h *THeap) Pop() T { 43 | heap.PopToLastF(len(*h), func(i, j int) bool { 44 | ti, tj := (*h)[i], (*h)h[j] 45 | // return whether ti < tj 46 | }, func(i, j int) { 47 | (*h)[i], (*h)[j] = (*h)[j], (*h)[i] 48 | }) 49 | 50 | res := (*h)[len(*h) - 1] 51 | *h = (*h)[:len(*h) - 1] 52 | 53 | return res 54 | } 55 | 56 | func (h *THeap) Push(x T) { 57 | *h = append(*h, x) 58 | heap.PushLastF(len(*h), func(i, j int) bool { 59 | ti, tj := (*h)[i], (*h)h[j] 60 | // return whether ti < tj 61 | }, func(i, j int) { 62 | (*h)[i], (*h)[j] = (*h)[j], (*h)[i] 63 | }) 64 | } 65 | */ 66 | package heap 67 | 68 | import "sort" 69 | 70 | // Init heapifies a non-empty array defined by the sort.Interface. 71 | // The complexity is O(N), where N = h.Len(). 72 | // 73 | func Init(h sort.Interface) { 74 | // heapify 75 | n := h.Len() 76 | for i := n/2 - 1; i >= 0; i-- { 77 | heapDown(n, h.Less, h.Swap, i) 78 | } 79 | } 80 | 81 | // Similar to Init but with interface provided by funcs. 82 | func InitF(Len int, Less func(i, j int) bool, Swap func(i, j int)) { 83 | // heapify 84 | for i := Len/2 - 1; i >= 0; i-- { 85 | heapDown(Len, Less, Swap, i) 86 | } 87 | } 88 | 89 | // Push pushes the last element of the heap, which was not considered as part 90 | // of the heap, onto the heap. 91 | // The complexity is O(log(N)), where N = h.Len(). 92 | // 93 | // NOTE You need to append the element to be pushed as the last element before 94 | // calling to this method. 95 | func PushLast(h sort.Interface) { 96 | heapUp(h.Less, h.Swap, h.Len()-1) 97 | } 98 | 99 | // Similar to PushLast but with interface provided by funcs. 100 | func PushLastF(Len int, Less func(i, j int) bool, Swap func(i, j int)) { 101 | heapUp(Less, Swap, Len-1) 102 | } 103 | 104 | // Pop removes the minimum element (according to Less) from the heap 105 | // and place it as the last element of the heap. 106 | // The complexity is O(log(N)), where N = h.Len(). 107 | // 108 | // Same as Remove(h, 0). 109 | // 110 | // NOTE You need to remove the last element after calling to this method. 111 | func PopToLast(h sort.Interface) { 112 | n1 := h.Len() - 1 113 | h.Swap(0, n1) 114 | heapDown(n1, h.Less, h.Swap, 0) 115 | } 116 | 117 | // Similar to PopToLast but with interface provided by funcs. 118 | func PopToLastF(Len int, Less func(i, j int) bool, Swap func(i, j int)) { 119 | Swap(0, Len-1) 120 | heapDown(Len-1, Less, Swap, 0) 121 | } 122 | 123 | // Fix re-establishes the heap ordering after the value of the element at the index has changed. 124 | // Changing the value of the element at the index and then calling Fix is equivalent to, 125 | // but less expensive than, calling RemoveToLast(h, index) followed by a PushLast. 126 | // The complexity is O(log(N)), where N = h.Len(). 127 | func Fix(h sort.Interface, index int) { 128 | heapDown(h.Len(), h.Less, h.Swap, index) 129 | heapUp(h.Less, h.Swap, index) 130 | } 131 | 132 | // Similar to Fix but with interface provided by funcs. 133 | func FixF(Len int, Less func(i, j int) bool, Swap func(i, j int), index int) { 134 | heapDown(Len, Less, Swap, index) 135 | heapUp(Less, Swap, index) 136 | } 137 | 138 | // Remove removes the element at index i from the heap and place it at the last 139 | // element of the heap. 140 | // 141 | // The complexity is O(log(n)) where n = h.Len(). 142 | // 143 | // NOTE You need to remove the last element after calling to this method. 144 | func RemoveToLast(h sort.Interface, i int) { 145 | n := h.Len() - 1 146 | if n != i { 147 | h.Swap(i, n) 148 | heapDown(n, h.Less, h.Swap, i) 149 | heapUp(h.Less, h.Swap, i) 150 | } 151 | } 152 | 153 | // Similar to RemoveToLast but with interface provided by funcs. 154 | func RemoveToLastF(Len int, Less func(i, j int) bool, Swap func(i, j int), i int) { 155 | n := Len - 1 156 | if n != i { 157 | Swap(i, n) 158 | heapDown(n, Less, Swap, i) 159 | heapUp(Less, Swap, i) 160 | } 161 | } 162 | 163 | func heapUp(less func(i, j int) bool, swap func(i, j int), i int) { 164 | for i > 0 { 165 | p := (i - 1) / 2 // p is the parent of i 166 | if !less(i, p) { 167 | // h[p] <= h[i], already in order 168 | break 169 | } 170 | swap(i, p) 171 | i = p // move to upper level 172 | } 173 | } 174 | 175 | func heapDown(n int, less func(i, j int) bool, swap func(i, j int), i int) { 176 | for { 177 | l := 2*i + 1 // left child 178 | if l >= n || l < 0 { 179 | break 180 | } 181 | c := l // c is initialized with l 182 | // Set c to r if h[r] < h[l] 183 | if r := l + 1; r < n && r > 0 && less(r, l) { 184 | c = r 185 | } 186 | if !less(c, i) { 187 | // h[i] <= h[c], already in order 188 | break 189 | } 190 | swap(i, c) 191 | i = c // move to lower level 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /heap/heap_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "container/heap" 5 | "math/rand" 6 | "sort" 7 | "testing" 8 | 9 | "github.com/golangplus/testing/assert" 10 | ) 11 | 12 | func TestInitF(t *testing.T) { 13 | l := []int{5, 9, 1, 3, 2} 14 | InitF(len(l), func(i, j int) bool { return l[i] < l[j] }, func(i, j int) { l[i], l[j] = l[j], l[i] }) 15 | assert.Equal(t, "l", l, []int{1, 2, 5, 3, 9}) 16 | } 17 | 18 | func TestFix(t *testing.T) { 19 | l := []int{1, 2, 5, 3, 9} 20 | l[0] = 4 21 | Fix(sort.IntSlice(l), 0) 22 | assert.Equal(t, "l", l, []int{2, 3, 5, 4, 9}) 23 | } 24 | 25 | func TestRemoveToLast(t *testing.T) { 26 | l := []int{1, 2, 5, 3, 9} 27 | RemoveToLast(sort.IntSlice(l), 0) 28 | assert.Equal(t, "l", l, []int{2, 3, 5, 9, 1}) 29 | } 30 | 31 | func TestRemoveToLastF(t *testing.T) { 32 | l := []int{1, 2, 5, 3, 9} 33 | RemoveToLastF(len(l), func(i, j int) bool { return l[i] < l[j] }, func(i, j int) { l[i], l[j] = l[j], l[i] }, 0) 34 | assert.Equal(t, "l", l, []int{2, 3, 5, 9, 1}) 35 | } 36 | 37 | func TestHeap_InitFromSlice(t *testing.T) { 38 | h := sort.IntSlice{6, 5, 4, 3} 39 | Init(h) 40 | } 41 | 42 | type intHeap []int 43 | 44 | func (h *intHeap) Pop() int { 45 | PopToLast((*sort.IntSlice)(h)) 46 | res := (*h)[len(*h)-1] 47 | *h = (*h)[:len(*h)-1] 48 | 49 | return res 50 | } 51 | 52 | func (h *intHeap) Push(x int) { 53 | *h = append(*h, x) 54 | PushLast((*sort.IntSlice)(h)) 55 | } 56 | 57 | func TestIntHeap(t *testing.T) { 58 | var h intHeap 59 | 60 | for i := 0; i < 1000; i++ { 61 | h.Push(rand.Int()) 62 | } 63 | 64 | assert.Equal(t, "len(h)", len(h), 1000) 65 | 66 | peek := h[0] 67 | last := h.Pop() 68 | assert.Equal(t, "h.Peek()", peek, last) 69 | for i := 1; i < 1000; i++ { 70 | cur := h.Pop() 71 | if cur < last { 72 | t.Errorf("%d should be larger than %d", cur, last) 73 | } 74 | last = cur 75 | } 76 | } 77 | 78 | func BenchmarkPlusIntHeapInter(b *testing.B) { 79 | var data [M]int 80 | for i := range data { 81 | data[i] = rand.Int() 82 | } 83 | b.StartTimer() 84 | for i := 0; i < b.N; i++ { 85 | var h intHeap 86 | for _, vl := range data { 87 | h.Push(vl) 88 | } 89 | for len(h) > 0 { 90 | h.Pop() 91 | } 92 | } 93 | } 94 | 95 | type Data struct { 96 | Value string 97 | Priority int 98 | } 99 | 100 | type DataHeap []Data 101 | 102 | func (h DataHeap) Len() int { return len(h) } 103 | func (h DataHeap) Less(i, j int) bool { return h[i].Priority < h[j].Priority } 104 | func (h DataHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 105 | 106 | func (h *DataHeap) Pop() Data { 107 | /* 108 | PopToLastF(len(*h), func(i, j int) bool { 109 | return (*h)[i].Priority < (*h)[j].Priority 110 | }, func(i, j int) { 111 | (*h)[i], (*h)[j] = (*h)[j], (*h)[i] 112 | }) 113 | */ 114 | PopToLast(h) 115 | 116 | res := (*h)[len(*h)-1] 117 | *h = (*h)[:len(*h)-1] 118 | 119 | return res 120 | } 121 | 122 | func (h *DataHeap) Push(x Data) { 123 | *h = append(*h, x) 124 | PushLast(h) 125 | /* 126 | PushLastF(len(*h), func(i, j int) bool { 127 | return (*h)[i].Priority < (*h)[j].Priority 128 | }, func(i, j int) { 129 | (*h)[i], (*h)[j] = (*h)[j], (*h)[i] 130 | })*/ 131 | } 132 | 133 | type DataHeapF []Data 134 | 135 | func (h *DataHeapF) Pop() Data { 136 | PopToLastF(len(*h), func(i, j int) bool { 137 | return (*h)[i].Priority < (*h)[j].Priority 138 | }, func(i, j int) { 139 | (*h)[i], (*h)[j] = (*h)[j], (*h)[i] 140 | }) 141 | 142 | res := (*h)[len(*h)-1] 143 | *h = (*h)[:len(*h)-1] 144 | 145 | return res 146 | } 147 | 148 | func (h *DataHeapF) Push(x Data) { 149 | *h = append(*h, x) 150 | PushLastF(len(*h), func(i, j int) bool { 151 | return (*h)[i].Priority < (*h)[j].Priority 152 | }, func(i, j int) { 153 | (*h)[i], (*h)[j] = (*h)[j], (*h)[i] 154 | }) 155 | } 156 | 157 | type builtinDataHeap []Data 158 | 159 | func (h builtinDataHeap) Len() int { return len(h) } 160 | func (h builtinDataHeap) Less(i, j int) bool { return h[i].Priority < h[j].Priority } 161 | func (h builtinDataHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 162 | 163 | func (h *builtinDataHeap) Push(x interface{}) { 164 | // Push and Pop use pointer receivers because they modify the slice's length, 165 | // not just its contents. 166 | *h = append(*h, x.(Data)) 167 | } 168 | 169 | func (h *builtinDataHeap) Pop() interface{} { 170 | old := *h 171 | n := len(old) 172 | x := old[n-1] 173 | *h = old[0 : n-1] 174 | return x 175 | } 176 | 177 | func TestDataHeap(t *testing.T) { 178 | var h DataHeap 179 | 180 | for i := 0; i < 1000; i++ { 181 | h.Push(Data{"A", rand.Int()}) 182 | } 183 | 184 | assert.Equal(t, "len(h)", len(h), 1000) 185 | 186 | peek := h[0] 187 | last := h.Pop() 188 | assert.Equal(t, "h.Peek()", peek, last) 189 | for i := 1; i < 1000; i++ { 190 | cur := h.Pop() 191 | if cur.Priority < last.Priority { 192 | t.Errorf("%v should be larger than %v", cur, last) 193 | } 194 | last = cur 195 | } 196 | } 197 | 198 | func BenchmarkDataHeap_Plus(b *testing.B) { 199 | var data [M]Data 200 | for i := range data { 201 | data[i].Priority = rand.Int() 202 | } 203 | b.StartTimer() 204 | for i := 0; i < b.N; i++ { 205 | var h DataHeap 206 | for _, vl := range data { 207 | h.Push(vl) 208 | } 209 | for len(h) > 0 { 210 | h.Pop() 211 | } 212 | } 213 | } 214 | 215 | func BenchmarkDataHeap_F(b *testing.B) { 216 | var data [M]Data 217 | for i := range data { 218 | data[i].Priority = rand.Int() 219 | } 220 | b.StartTimer() 221 | for i := 0; i < b.N; i++ { 222 | var h DataHeapF 223 | for _, vl := range data { 224 | h.Push(vl) 225 | } 226 | for len(h) > 0 { 227 | h.Pop() 228 | } 229 | } 230 | } 231 | 232 | func BenchmarkDataHeap_Pkg(b *testing.B) { 233 | var data [M]Data 234 | for i := range data { 235 | data[i].Priority = rand.Int() 236 | } 237 | b.StartTimer() 238 | for i := 0; i < b.N; i++ { 239 | var h builtinDataHeap 240 | for _, vl := range data { 241 | heap.Push(&h, vl) 242 | } 243 | for len(h) > 0 { 244 | heap.Pop(&h) 245 | } 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /heap/interfaces.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | // A heap for interface{}. Use NewInterfaces to create an instance. 4 | type Interfaces interface { 5 | // Len returns the number of elements in the current heap. 6 | Len() int 7 | // Push inserts an element to the heap. 8 | Push(x interface{}) 9 | // Pop removes the top element from the heap and returns it. 10 | Pop() interface{} 11 | // PopAll pops and returns all elements of the heap in reverse order. 12 | PopAll() []interface{} 13 | // Peek returns the top most element. It panics if the heap is empty. 14 | Peek() interface{} 15 | // TopNPush inserts an element to the heap if the heap does not reach its 16 | // capacity. Otherwise, if the top element is less then the new element 17 | // the top element is removed and the new one is inserted. 18 | // This method is used to generate a top N largest elements where N is 19 | // the capacity of the heap. 20 | TopNPush(x interface{}) 21 | // TopNPopAll is similar to PopAll but keep the capacity of the heap 22 | // unchanged. 23 | TopNPopAll() []interface{} 24 | } 25 | 26 | type interfaces struct { 27 | less func(i, j int) bool 28 | list []interface{} 29 | } 30 | 31 | // Interfaces.Len 32 | func (h *interfaces) Len() int { return len(h.list) } 33 | 34 | // Interfaces.Push 35 | func (h *interfaces) Push(x interface{}) { 36 | h.list = append(h.list, x) 37 | 38 | PushLastF(len(h.list), h.less, func(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] }) 39 | } 40 | 41 | // Interfaces.TopNPush 42 | func (h *interfaces) TopNPush(x interface{}) { 43 | if len(h.list) < cap(h.list)-1 { 44 | h.Push(x) 45 | return 46 | } 47 | h.list = append(h.list, x) 48 | if h.less(0, cap(h.list)-1) { 49 | h.list[0], h.list[cap(h.list)-1] = x, nil 50 | FixF(cap(h.list)-1, h.less, func(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] }, 0) 51 | } 52 | h.list = h.list[:cap(h.list)-1] 53 | } 54 | 55 | // Interfaces.Pop 56 | func (h *interfaces) Pop() interface{} { 57 | PopToLastF(len(h.list), h.less, func(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] }) 58 | 59 | res := h.list[len(h.list)-1] 60 | h.list[len(h.list)-1] = nil // remove the reference in h.list 61 | h.list = h.list[:len(h.list)-1] 62 | 63 | return res 64 | } 65 | 66 | // Interfaces.PopAll 67 | func (h *interfaces) PopAll() []interface{} { 68 | for n := h.Len(); n > 1; n-- { 69 | PopToLastF(n, h.less, func(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] }) 70 | } 71 | res := h.list 72 | h.list = nil 73 | return res 74 | } 75 | 76 | // Interfaces.TopNPopAll 77 | func (h *interfaces) TopNPopAll() []interface{} { 78 | for n := h.Len(); n > 1; n-- { 79 | PopToLastF(n, h.less, func(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] }) 80 | } 81 | res := append([]interface{}(nil), h.list...) 82 | for i := range h.list { 83 | h.list[i] = nil // remove the reference in h.list 84 | } 85 | h.list = h.list[:0] 86 | return res 87 | } 88 | 89 | // Interfaces.Peek 90 | func (h *interfaces) Peek() interface{} { 91 | return h.list[0] 92 | } 93 | 94 | // NewInterfaces returns an instance of Interfaces with a customized less func and the initial capacity. 95 | func NewInterfaces(less func(x, y interface{}) bool, cap int) Interfaces { 96 | h := &interfaces{} 97 | 98 | h.less = func(i, j int) bool { 99 | return less(h.list[i], h.list[j]) 100 | } 101 | if cap > 0 { 102 | h.list = make([]interface{}, 0, cap+1) 103 | } 104 | 105 | return h 106 | } 107 | -------------------------------------------------------------------------------- /heap/ints.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | // A heap for ints. The pointer to the zero value of Ints is a heap with default less 8 | // func which compares int values on the natual order. 9 | // Use NewInts to customize less func and initial capacity. 10 | type Ints struct { 11 | // Less func given indexes of i, j in the list slice. 12 | less func(i, j int) bool 13 | list sort.IntSlice 14 | } 15 | 16 | // Len returns the number of elements in the current heap. 17 | func (h *Ints) Len() int { 18 | return len(h.list) 19 | } 20 | 21 | // Push inserts an element to the heap. 22 | func (h *Ints) Push(x int) { 23 | h.list = append(h.list, x) 24 | 25 | if h.less == nil { 26 | PushLastF(len(h.list), h.list.Less, h.list.Swap) 27 | } else { 28 | PushLastF(len(h.list), h.less, h.list.Swap) 29 | } 30 | } 31 | 32 | // Peek returns the top most element. It panics if the heap is empty. 33 | func (h *Ints) Peek() int { 34 | return h.list[0] 35 | } 36 | 37 | // Pop removes the top element from the heap and returns it. 38 | func (h *Ints) Pop() int { 39 | if h.less == nil { 40 | PopToLastF(len(h.list), h.list.Less, h.list.Swap) 41 | } else { 42 | PopToLastF(len(h.list), h.less, h.list.Swap) 43 | } 44 | 45 | res := h.list[len(h.list)-1] 46 | h.list = h.list[:len(h.list)-1] 47 | 48 | return res 49 | } 50 | 51 | // PopAll pops and returns all elements of the heap in reverse order. 52 | func (h *Ints) PopAll() []int { 53 | for n := h.Len(); n > 1; n-- { 54 | if h.less == nil { 55 | PopToLastF(n, h.list.Less, h.list.Swap) 56 | } else { 57 | PopToLastF(n, h.less, h.list.Swap) 58 | } 59 | } 60 | res := h.list 61 | h.list = nil 62 | return res 63 | } 64 | 65 | // NewInts returns a *Ints with customized less func and initial capacity. 66 | func NewInts(less func(x, y int) bool, cap int) *Ints { 67 | h := &Ints{} 68 | 69 | if less != nil { 70 | h.less = func(i, j int) bool { 71 | return less(h.list[i], h.list[j]) 72 | } 73 | } 74 | if cap > 0 { 75 | h.list = make(sort.IntSlice, 0, cap) 76 | } 77 | 78 | return h 79 | } 80 | -------------------------------------------------------------------------------- /heap/ints_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "container/heap" 5 | "math/rand" 6 | "testing" 7 | 8 | "github.com/golangplus/testing/assert" 9 | ) 10 | 11 | func TestInts_DefLess(t *testing.T) { 12 | var h Ints 13 | 14 | assert.Equal(t, "len", h.Len(), 0) 15 | 16 | h.Push(5) 17 | h.Push(2) 18 | h.Push(1) 19 | h.Push(3) 20 | 21 | assert.Equal(t, "len", h.Len(), 4) 22 | assert.Equal(t, "peek", h.Peek(), 1) 23 | 24 | res := []int{h.Pop(), h.Pop(), h.Pop(), h.Pop()} 25 | assert.Equal(t, "res", res, []int{1, 2, 3, 5}) 26 | 27 | h.Push(5) 28 | h.Push(2) 29 | h.Push(1) 30 | h.Push(3) 31 | assert.Equal(t, "PopAll", h.PopAll(), []int{5, 3, 2, 1}) 32 | } 33 | 34 | func TestInts_CustomLess(t *testing.T) { 35 | data := [...]int{5, 2, 1, 3} 36 | 37 | h := NewInts(func(i, j int) bool { 38 | return data[i] < data[j] 39 | }, 5) 40 | 41 | assert.Equal(t, "len", h.Len(), 0) 42 | 43 | h.Push(0) 44 | h.Push(1) 45 | h.Push(2) 46 | h.Push(3) 47 | 48 | assert.Equal(t, "len", h.Len(), 4) 49 | assert.Equal(t, "peek", h.Peek(), 2) 50 | res := []int{h.Pop(), h.Pop(), h.Pop(), h.Pop()} 51 | assert.Equal(t, "res", res, []int{2, 1, 3, 0}) 52 | 53 | h.Push(0) 54 | h.Push(1) 55 | h.Push(2) 56 | h.Push(3) 57 | assert.Equal(t, "PopAll", h.PopAll(), []int{0, 3, 1, 2}) 58 | } 59 | 60 | type builtinIntHeap []int 61 | 62 | func (h builtinIntHeap) Len() int { return len(h) } 63 | func (h builtinIntHeap) Less(i, j int) bool { return h[i] < h[j] } 64 | func (h builtinIntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 65 | 66 | func (h *builtinIntHeap) Push(x interface{}) { 67 | // Push and Pop use pointer receivers because they modify the slice's length, 68 | // not just its contents. 69 | *h = append(*h, x.(int)) 70 | } 71 | 72 | func (h *builtinIntHeap) Pop() interface{} { 73 | old := *h 74 | n := len(old) 75 | x := old[n-1] 76 | *h = old[0 : n-1] 77 | return x 78 | } 79 | 80 | const M = 10000 81 | 82 | func BenchmarkBuiltinIntHeap(b *testing.B) { 83 | var data [M]int 84 | for i := range data { 85 | data[i] = rand.Int() 86 | } 87 | b.StartTimer() 88 | for i := 0; i < b.N; i++ { 89 | var h builtinIntHeap 90 | for _, vl := range data { 91 | heap.Push(&h, vl) 92 | } 93 | for len(h) > 0 { 94 | heap.Pop(&h) 95 | } 96 | } 97 | } 98 | 99 | func BenchmarkPlusIntHeap(b *testing.B) { 100 | var data [M]int 101 | for i := range data { 102 | data[i] = rand.Int() 103 | } 104 | b.StartTimer() 105 | for i := 0; i < b.N; i++ { 106 | var h Ints 107 | for _, vl := range data { 108 | h.Push(vl) 109 | } 110 | for h.Len() > 0 { 111 | h.Pop() 112 | } 113 | } 114 | } 115 | 116 | func BenchmarkPlusIntHeap_Less(b *testing.B) { 117 | var data [M]int 118 | for i := range data { 119 | data[i] = rand.Int() 120 | } 121 | b.StartTimer() 122 | for i := 0; i < b.N; i++ { 123 | h := NewInts(func(x, y int) bool { 124 | return x < y 125 | }, 0) 126 | for _, vl := range data { 127 | h.Push(vl) 128 | } 129 | for h.Len() > 0 { 130 | h.Pop() 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /heap/strings.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | // A heap for strings. The pointer to the zero value of Strings is a heap with default less 8 | // func which compares string values on the natual order. 9 | // Use NewStrings to customize less func and initial capacity. 10 | type Strings struct { 11 | less func(i, j int) bool 12 | list sort.StringSlice 13 | } 14 | 15 | // Len returns the number of elements in the current heap. 16 | func (h *Strings) Len() int { 17 | return len(h.list) 18 | } 19 | 20 | // Push inserts an element to the heap. 21 | func (h *Strings) Push(x string) { 22 | h.list = append(h.list, x) 23 | 24 | if h.less == nil { 25 | PushLastF(len(h.list), h.list.Less, h.list.Swap) 26 | } else { 27 | PushLastF(len(h.list), h.less, h.list.Swap) 28 | } 29 | } 30 | 31 | // Peek returns the top most element. It panics if the heap is empty. 32 | func (h *Strings) Peek() string { 33 | return h.list[0] 34 | } 35 | 36 | // Pop removes the top element from the heap and returns it. 37 | func (h *Strings) Pop() string { 38 | if h.less == nil { 39 | PopToLastF(len(h.list), h.list.Less, h.list.Swap) 40 | } else { 41 | PopToLastF(len(h.list), h.less, h.list.Swap) 42 | } 43 | 44 | res := h.list[len(h.list)-1] 45 | h.list[len(h.list)-1] = "" // remove the reference in h.list 46 | h.list = h.list[:len(h.list)-1] 47 | 48 | return res 49 | } 50 | 51 | // PopAll pops and returns all elements of the heap in reverse order. 52 | func (h *Strings) PopAll() []string { 53 | for n := h.Len(); n > 1; n-- { 54 | if h.less == nil { 55 | PopToLastF(n, h.list.Less, h.list.Swap) 56 | } else { 57 | PopToLastF(n, h.less, h.list.Swap) 58 | } 59 | } 60 | res := h.list 61 | h.list = nil 62 | return res 63 | } 64 | 65 | // NewStrings returns a *Strings with a customized less func and the initial capacity. 66 | func NewStrings(less func(x, y string) bool, cap int) *Strings { 67 | h := &Strings{} 68 | 69 | if less != nil { 70 | h.less = func(i, j int) bool { 71 | return less(h.list[i], h.list[j]) 72 | } 73 | } 74 | if cap > 0 { 75 | h.list = make([]string, 0, cap) 76 | } 77 | return h 78 | } 79 | -------------------------------------------------------------------------------- /heap/strings_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golangplus/testing/assert" 7 | ) 8 | 9 | func TestStrings_DefLess(t *testing.T) { 10 | var h Strings 11 | 12 | assert.Equal(t, "len", h.Len(), 0) 13 | 14 | h.Push("Elmo") 15 | h.Push("Big Bird") 16 | h.Push("Abby") 17 | h.Push("Count") 18 | 19 | assert.Equal(t, "len", h.Len(), 4) 20 | assert.Equal(t, "peek", h.Peek(), "Abby") 21 | 22 | res := []string{h.Pop(), h.Pop(), h.Pop(), h.Pop()} 23 | assert.Equal(t, "res", res, []string{"Abby", "Big Bird", "Count", "Elmo"}) 24 | 25 | h.Push("Elmo") 26 | h.Push("Big Bird") 27 | h.Push("Abby") 28 | h.Push("Count") 29 | assert.Equal(t, "PopAll", h.PopAll(), []string{"Elmo", "Count", "Big Bird", "Abby"}) 30 | } 31 | 32 | func TestStrings_UnRef(t *testing.T) { 33 | var h Strings 34 | h.Push("Hello") 35 | h.Pop() 36 | assert.Equal(t, "h.list[0]", h.list[:1][0], "") 37 | } 38 | 39 | func TestStrings_CustomLess(t *testing.T) { 40 | data := map[string]int{ 41 | "Abby": 5, 42 | "Big Bird": 2, 43 | "Count": 1, 44 | "Elmo": 3, 45 | } 46 | 47 | h := NewStrings(func(x, y string) bool { 48 | return data[x] < data[y] 49 | }, 5) 50 | 51 | assert.Equal(t, "len", h.Len(), 0) 52 | 53 | h.Push("Abby") 54 | h.Push("Big Bird") 55 | h.Push("Count") 56 | h.Push("Elmo") 57 | 58 | assert.Equal(t, "len", h.Len(), 4) 59 | assert.Equal(t, "peek", h.Peek(), "Count") 60 | res := []string{h.Pop(), h.Pop(), h.Pop(), h.Pop()} 61 | assert.StringEqual(t, "res", res, []string{"Count", "Big Bird", "Elmo", "Abby"}) 62 | 63 | h.Push("Abby") 64 | h.Push("Big Bird") 65 | h.Push("Count") 66 | h.Push("Elmo") 67 | assert.Equal(t, "PopAll", h.PopAll(), []string{"Abby", "Elmo", "Big Bird", "Count"}) 68 | } 69 | --------------------------------------------------------------------------------