Skip to content

Commit

Permalink
gcontainers: gheap,glist,gmap,gslice etc..
Browse files Browse the repository at this point in the history
  • Loading branch information
miniLCT committed Apr 7, 2024
1 parent 4ec82c0 commit c8d2295
Show file tree
Hide file tree
Showing 15 changed files with 1,714 additions and 0 deletions.
96 changes: 96 additions & 0 deletions gcontainers/gheap/heap.go
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
}
81 changes: 81 additions & 0 deletions gcontainers/gheap/heap_test.go
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))
}
}
87 changes: 87 additions & 0 deletions gcontainers/glist/list.go
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
}
}
42 changes: 42 additions & 0 deletions gcontainers/glist/list_test.go
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)
}
Loading

0 comments on commit c8d2295

Please sign in to comment.