-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gcontainers: gheap,glist,gmap,gslice etc..
- Loading branch information
Showing
15 changed files
with
1,714 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package gheap | ||
|
||
import "github.com/miniLCT/gosb/gogenerics/constraints" | ||
|
||
// Heap is the generics implementation of heap | ||
|
||
type Heap[T any] struct { | ||
data []T | ||
less constraints.Less[T] | ||
} | ||
|
||
// New constructs a new heap | ||
func New[T any](less constraints.Less[T]) *Heap[T] { | ||
return &Heap[T]{ | ||
data: make([]T, 0), | ||
less: less, | ||
} | ||
} | ||
|
||
// NewWithData build a heap tree with data | ||
func NewWithData[T any](data []T, less constraints.Less[T]) *Heap[T] { | ||
h := New(less) | ||
h.data = data | ||
heapSort(h.data, less) | ||
return h | ||
} | ||
|
||
// siftDown implements the heap property on v[lo:hi]. | ||
func siftDown[T any](x []T, index int, less constraints.Less[T]) { | ||
for { | ||
left := (index * 2) + 1 | ||
right := left + 1 | ||
if left >= len(x) { | ||
break | ||
} | ||
c := left | ||
if len(x) > right && less(x[right], x[left]) { | ||
c = right | ||
} | ||
if less(x[index], x[c]) { | ||
break | ||
} | ||
x[c], x[index] = x[index], x[c] | ||
index = c | ||
} | ||
} | ||
|
||
func siftUp[T any](x []T, index int, less constraints.Less[T]) { | ||
for index > 0 { | ||
p := (index - 1) / 2 | ||
if less(x[p], x[index]) { | ||
break | ||
} | ||
x[p], x[index] = x[index], x[p] | ||
index = p | ||
} | ||
} | ||
|
||
// heapSort is min-heap sort | ||
func heapSort[T any](v []T, less constraints.Less[T]) { | ||
n := len(v) | ||
for i := n/2 - 1; i >= 0; i-- { | ||
siftDown(v, i, less) | ||
} | ||
|
||
// Build heap with greatest element at top. | ||
// for i := (len(v) - 1) / 2; i >= 0; i-- { | ||
// siftDown(v, i, len(v), less) | ||
// } | ||
|
||
// // Pop elements into end of v. | ||
// for i := len(v) - 1; i >= 1; i-- { | ||
// v[0], v[i] = v[i], v[0] | ||
// siftDown(v[:i], 0, len(v), less) // BUG | ||
// } | ||
} | ||
|
||
// Push pushes the element v onto the heap. | ||
func Push[T any](h *Heap[T], v T) { | ||
x := &h.data | ||
(*x) = append((*x), v) | ||
siftUp(*x, len(*x)-1, h.less) | ||
h.data = (*x) | ||
} | ||
|
||
// Pop removes the minimum element from the heap and returns it. | ||
func Pop[T any](h *Heap[T]) T { | ||
x := &h.data | ||
ret := (*x)[0] | ||
(*x)[0], *x = (*x)[len(*x)-1], (*x)[:len(*x)-1] | ||
if len(*x) > 0 { | ||
siftDown((*x), 0, h.less) | ||
} | ||
h.data = (*x) | ||
return ret | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package gheap | ||
|
||
import ( | ||
"math/rand" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/miniLCT/gosb/gcontainers/gslice" | ||
) | ||
|
||
var charSet = []string{ | ||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", | ||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", | ||
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", | ||
} | ||
|
||
func genString() string { | ||
l := 4 | ||
var str string | ||
for i := 0; i < l; i++ { | ||
str += charSet[rand.Intn(len(charSet))] | ||
} | ||
return str | ||
} | ||
|
||
type Node struct { | ||
Name string | ||
Count int | ||
} | ||
|
||
func TestHeap(t *testing.T) { | ||
assert := assert.New(t) | ||
t.Parallel() | ||
|
||
rand.Seed(time.Now().Unix()) | ||
nds := []*Node{} | ||
for i := 0; i < 10; i++ { | ||
nds = append(nds, &Node{ | ||
Name: genString(), | ||
Count: rand.Intn(4), | ||
}) | ||
} | ||
less := func(a, b *Node) bool { | ||
if a.Count != b.Count { | ||
return a.Count > b.Count | ||
} | ||
return a.Name < b.Name | ||
} | ||
hp := NewWithData(nds, less) | ||
|
||
tmp := []*Node{} | ||
for i := 0; i <= 100; i++ { | ||
Push(hp, &Node{ | ||
Name: genString(), | ||
Count: rand.Intn(4), | ||
}) | ||
for len(hp.data) > 0 { | ||
v := Pop(hp) | ||
tmp = append(tmp, v) | ||
} | ||
assert.True(gslice.IsSortedFunc(tmp, less)) | ||
for _, v := range tmp { | ||
Push(hp, v) | ||
} | ||
tmp = make([]*Node, 0) | ||
} | ||
|
||
for i := 0; i <= 100; i++ { | ||
tmpSlice := gslice.Copy(hp.data) | ||
gslice.Shuffle(tmpSlice) | ||
h := NewWithData(tmpSlice[:rand.Int()%len(tmpSlice)], less) | ||
tpchecks := make([]*Node, 0) | ||
for len(h.data) > 0 { | ||
v := Pop(h) | ||
tpchecks = append(tpchecks, v) | ||
} | ||
assert.True(gslice.IsSortedFunc(tpchecks, less)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package glist | ||
|
||
// Node is a node in the linked list | ||
type Node[T any] struct { | ||
Value T | ||
Prev, Next *Node[T] | ||
} | ||
|
||
// List is a doubly-linked list | ||
type List[T any] struct { | ||
Front *Node[T] | ||
Back *Node[T] | ||
} | ||
|
||
// New returns an empty linked list | ||
func New[T any]() *List[T] { | ||
return &List[T]{} | ||
} | ||
|
||
// PushBack adds v to the end of the list | ||
func PushBack[T any](l *List[T], v T) { | ||
PushBackNode(l, &Node[T]{ | ||
Value: v, | ||
}) | ||
} | ||
|
||
// PushBackNode adds the node nd to the back of the list | ||
func PushBackNode[T any](l *List[T], nd *Node[T]) { | ||
nd.Next = nil | ||
nd.Prev = l.Back | ||
if l.Back != nil { | ||
l.Back.Next = nd | ||
} else { | ||
l.Front = nd | ||
} | ||
l.Back = nd | ||
} | ||
|
||
// PushFront adds v to the beginning of the list | ||
func PushFront[T any](l *List[T], v T) { | ||
PushFrontNode(l, &Node[T]{ | ||
Value: v, | ||
}) | ||
} | ||
|
||
// PushFrontNode adds the node nd to the beginning of the list | ||
func PushFrontNode[T any](l *List[T], nd *Node[T]) { | ||
nd.Next = l.Front | ||
nd.Prev = nil | ||
if l.Front != nil { | ||
l.Front.Prev = nd | ||
} else { | ||
l.Back = nd | ||
} | ||
l.Front = nd | ||
} | ||
|
||
// Remove removes the node nd from the list | ||
func Remove[T any](l *List[T], nd *Node[T]) { | ||
if nd.Next != nil { | ||
nd.Next.Prev = nd.Prev | ||
} else { | ||
l.Back = nd.Prev | ||
} | ||
|
||
if nd.Prev != nil { | ||
nd.Prev.Next = nd.Next | ||
} else { | ||
l.Front = nd.Next | ||
} | ||
} | ||
|
||
// Range iterates over the list and calls f for each element | ||
func Range[T any](nd *Node[T], f func(T)) { | ||
for nd != nil { | ||
f(nd.Value) | ||
nd = nd.Next | ||
} | ||
} | ||
|
||
// RangeReverse iterates over the list in reverse order and calls f for each element | ||
func RangeReverse[T any](nd *Node[T], f func(T)) { | ||
for nd != nil { | ||
f(nd.Value) | ||
nd = nd.Prev | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package glist | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestList(t *testing.T) { | ||
l := New[int]() | ||
for i := 0; i < 5; i++ { | ||
PushFront(l, i) | ||
} | ||
for i := 0; i < 5; i++ { | ||
PushBack(l, i) | ||
} | ||
s1 := make([]int, 0, 10) | ||
Range(l.Front, func(i int) { | ||
s1 = append(s1, i) | ||
}) | ||
s2 := make([]int, 0, 10) | ||
RangeReverse(l.Back, func(i int) { | ||
s2 = append(s2, i) | ||
}) | ||
t.Logf("s1=%v\n", s1) | ||
t.Logf("s2=%v\n", s2) | ||
|
||
assert := assert.New(t) | ||
assert.Equal(s2, s1) | ||
|
||
Remove(l, l.Back) | ||
Remove(l, l.Front) | ||
|
||
lenL := 0 | ||
Range(l.Front, func(i int) { | ||
lenL++ | ||
}) | ||
assert.Equal(8, lenL) | ||
|
||
assert.Equal(3, l.Front.Value) | ||
assert.Equal(3, l.Back.Value) | ||
} |
Oops, something went wrong.