go language data structure and algorithm library GoSTL

Keywords: Go github less

GoSTL

GoSTL is a go language data structure and algorithm library, similar to C + + STL, but more powerful. Combined with the characteristics of go language, most of the data structures have realized thread safety. When creating objects, you can specify whether to turn it on through configuration parameters.

Function list

Example

slice

The slice in this library is a redefinition of go native slice.
Here is an example of how to turn a go native slice into an IntSlice and then sort it.

package main

import (
   "fmt"
   "github.com/liyue201/gostl/algorithm/sort"
   "github.com/liyue201/gostl/ds/slice"
   "github.com/liyue201/gostl/utils/comparator"
)

func main() {
   a := slice.IntSlice(make([]int, 0))
   a = append(a, 2)
   a = append(a, 1)
   a = append(a, 3)
   fmt.Printf("%v\n", a)
   
   // sort in ascending
   sort.Sort(a.Begin(), a.End())
   fmt.Printf("%v\n", a)
   
   // sort in descending
   sort.Sort(a.Begin(), a.End(), comparator.Reverse(comparator.BuiltinTypeComparator))
   fmt.Printf("%v\n", a)
}

array

Array is a data structure with fixed length once it is created, which supports random access and iterator access.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/array"
)

func main() {
    a := array.New(5)
    for i := 0; i < a.Size(); i++ {
        a.Set(i, i + 1)
    }
    for i := 0; i < a.Size(); i++ {
        fmt.Printf("%v ", a.At(i))
    }

    fmt.Printf("\n")
    for iter := a.Begin(); iter.IsValid(); iter.Next() {
        fmt.Printf("%v ", iter.Value())
    }
}

vector

Vector is a kind of data structure whose size can be automatically expanded, which is realized by slicing internally. Support random access and iterator access.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/algorithm/sort"
    "github.com/liyue201/gostl/ds/vector"
    "github.com/liyue201/gostl/utils/comparator"
)

func main() {
    v := vector.New()
    v.PushBack(1)
    v.PushBack(2)
    v.PushBack(3)
    for i := 0; i < v.Size(); i++ {
        fmt.Printf("%v ", v.At(i))
    }
    fmt.Printf("\n")

    // sort in descending
    sort.Sort(v.Begin(), v.End(), comparator.Reverse(comparator.BuiltinTypeComparator))
    for iter := v.Begin(); iter.IsValid(); iter.Next() {
        fmt.Printf("%v ", iter.Value())
    }
}

List (list)

  • Simple list

Simple list is a one-way list, which supports inserting data from the head and tail, and only traversing data from the head.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/list/simple_list"
)

func main() {
    l := simple_list.New()
    l.PushBack(1)
    l.PushFront(2)
    l.PushFront(3)
    l.PushBack(4)
    for n := l.FrontNode(); n != nil; n = n.Next() {
        fmt.Printf("%v ", n.Value)
    }
}
  • Two way list

Bi directional list, supports inserting data from the head and tail, and traversing data from the head and tail.

package main

import (
    "fmt"
    list "github.com/liyue201/gostl/ds/list/bid_list"
)

func main() {
    l := list.New()
    l.PushBack(1)
    l.PushFront(2)
    l.PushFront(3)
    l.PushBack(4)
    for n := l.FrontNode(); n != nil; n = n.Next() {
        fmt.Printf("%v ", n.Value)
    }
    fmt.Printf("\n", )

    for n := l.BackNode(); n != nil; n = n.Prev() {
        fmt.Printf("%v ", n.Value)
    }
}

Dual end queue (deque)

The dual end queue supports efficient data insertion from the head and tail, random access and iterator access.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/algorithm/sort"
    "github.com/liyue201/gostl/ds/deque"
)

func main() {
    q := deque.New()
    q.PushBack(2)
    q.PushFront(1)
    q.PushBack(3)
    q.PushFront(4)
    fmt.Printf("%v\n", q)

    sort.Sort(q.Begin(), q.End())
    fmt.Printf("%v\n", q)
    fmt.Printf("%v\n", q.PopBack())
    fmt.Printf("%v\n", q.PopFront())
    fmt.Printf("%v\n", q)
}

queue

Queue is a first in, first out data structure. The bottom layer uses the double end queue or linked list as the container. By default, the double end queue is used. If you want to use the linked list, you can use the queue.WithListContainer() parameter when creating an object. Thread safety is supported.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/queue"
    "sync"
    "time"
)

func example1()  {
    fmt.Printf("example1:\n")
    q := queue.New()
    for i := 0; i < 5; i++ {
        q.Push(i)
    }
    for !q.Empty() {
        fmt.Printf("%v\n", q.Pop())
    }
}

//  Linked list
func example2()  {
    fmt.Printf("example2:\n")
    q := queue.New(queue.WithListContainer())
    for i := 0; i < 5; i++ {
        q.Push(i)
    }
    for !q.Empty() {
        fmt.Printf("%v\n", q.Pop())
    }
}

// Thread safety
func example3() {
    fmt.Printf("example3:\n")

    s := queue.New(queue.WithThreadSave())
    sw := sync.WaitGroup{}
    sw.Add(2)
    go func() {
        defer sw.Done()
        for i := 0; i < 10; i++ {
            s.Push(i)
            time.Sleep(time.Microsecond * 100)
        }
    }()

    go func() {
        defer sw.Done()
        for i := 0; i < 10; {
            if !s.Empty() {
                val := s.Pop()
                fmt.Printf("%v\n", val)
                i++
            } else {
                time.Sleep(time.Microsecond * 100)
            }
        }
    }()
    sw.Wait()
}

func main() {
    example1()
    example2()
    example3()
}

Priority queue

Priority queue is based on the container/heap package of go standard library, which supports thread safety.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/priority_queue"
    "github.com/liyue201/gostl/utils/comparator"
)

func main() {
    q := priority_queue.New(priority_queue.WithComparator(comparator.Reverse(comparator.BuiltinTypeComparator)),
        priority_queue.WithThreadSave())
    q.Push(5)
    q.Push(13)
    q.Push(7)
    q.Push(9)
    q.Push(0)
    q.Push(88)
    for !q.Empty() {
        fmt.Printf("%v\n", q.Pop())
    }
}

stack

The stack is a last in, first out data structure. The bottom layer uses a double end queue or a linked list as a container. By default, the double end queue is used. If you want to use a linked list, you can use the queue.WithListContainer() parameter when creating an object. Thread safety is supported.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/stack"
    "sync"
    "time"
)

func example1() {
    fmt.Printf("example1:\n")

    s := stack.New()
    s.Push(1)
    s.Push(2)
    s.Push(3)
    for !s.Empty() {
        fmt.Printf("%v\n", s.Pop())
    }
}

// based on list
func example2() {
    fmt.Printf("example2:\n")

    s := stack.New(stack.WithListContainer())
    s.Push(1)
    s.Push(2)
    s.Push(3)
    for !s.Empty() {
        fmt.Printf("%v\n", s.Pop())
    }
}

// thread-save
func example3() {
    fmt.Printf("example3:\n")

    s := stack.New(stack.WithThreadSave())
    sw := sync.WaitGroup{}
    sw.Add(2)
    go func() {
        defer sw.Done()
        for i := 0; i < 10; i++ {
            s.Push(i)
            time.Sleep(time.Microsecond * 100)
        }
    }()

    go func() {
        defer sw.Done()
        for i := 0; i < 10; {
            if !s.Empty() {
                val := s.Pop()
                fmt.Printf("%v\n", val)
                i++
            } else {
                time.Sleep(time.Microsecond * 100)
            }
        }
    }()
    sw.Wait()
}

func main() {
    example1()
    example2()
    example3()
}

Red black tree

Red black tree is a balanced binary sort tree, which is used to insert and find data efficiently.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/rbtree"
)

func main()  {
    tree := rbtree.New()
    tree.Insert(1, "aaa")
    tree.Insert(5, "bbb")
    tree.Insert(3, "ccc")
    fmt.Printf("find %v returns %v\n",5, tree.Find(5))

    tree.Traversal(func(key, value interface{}) bool {
        fmt.Printf("%v : %v\n", key, value)
        return true
    })
    tree.Delete(tree.FindNode(3))
}

Map (map)

The mapping bottom layer is implemented by using red black tree, and supports iterative access in key order, which is different from the go native map type (the go native map bottom layer is hash, and does not support iterative access in key order). Thread safety is supported.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/map"
)

func main() {
    m := treemap.New(treemap.WithThreadSave())

    m.Insert("a", "aaa")
    m.Insert("b", "bbb")

    fmt.Printf("a = %v\n", m.Get("a"))
    fmt.Printf("b = %v\n", m.Get("b"))
    
    m.Erase("b")
}

Set (set)

The collection bottom layer is implemented by red black tree, which supports thread safety. Support basic operations of set, such as union, intersection and difference. Thread safety is supported.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/set"
)

func main()  {
    s := set.New(set.WithThreadSave())
    s.Insert(1)
    s.Insert(5)
    s.Insert(3)
    s.Insert(4)
    s.Insert(2)

    s.Erase(4)

    for iter := s.Begin(); iter.IsValid(); iter.Next() {
        fmt.Printf("%v\n", iter.Value())
    }

    fmt.Printf("%v\n", s.Contains(3))
    fmt.Printf("%v\n", s.Contains(10))
}

Bit map

Bit mapping is used to quickly mark and find whether a non negative integer is in a set. It takes up less memory than map or array.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/bitmap"
)

func main() {
    bm := bitmap.New(1000)
    bm.Set(6)
    bm.Set(10)

    fmt.Printf("%v\n", bm.IsSet(5))
    fmt.Printf("%v\n", bm.IsSet(6))
    bm.Unset(6)
    fmt.Printf("%v\n", bm.IsSet(6))
}

Bloom? Filter

The Boolean filter is used to quickly determine whether the data is in the collection. The bottom layer is implemented with bitmap, which occupies less memory space than map. The disadvantage is that it does not support deletion and has a certain error rate. Thread safety is supported. Support data export and reconstruction through exported data.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/bloomfilter"
)

func main() {
    filter := bloom.New(100, 4, bloom.WithThreadSave())
    filter.Add("hhhh")
    filter.Add("gggg")

    fmt.Printf("%v\n", filter.Contains("aaaa"))
    fmt.Printf("%v\n", filter.Contains("gggg"))
}

Hash map tree (hamt)

Compared with the traditional hash (open address method or linked list method hash), hash mapping tree has lower probability of hash conflict and higher space utilization. The time complexity of capacity expansion is low. Thread safety is supported.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/hamt"
)

func main() {
    h := hamt.New()
    // h := hamt.New(hamt.WithThreadSave())
    key := []byte("aaaaa")
    val := "bbbbbbbbbbbbb"

    h.Insert(key, val)
    fmt.Printf("%v = %v\n", string(key), h.Get(key))

    h.Erase(key)
    fmt.Printf("%v = %v\n", string(key), h.Get(key))
}

Consistent hash (ketama)

Consistent hash ketama algorithm, using 64 bit hash function and map storage, has less conflict probability. Thread safety is supported.

package main

import (
    "github.com/liyue201/gostl/ds/ketama"
    "fmt"
)

func main() {
    k := ketama.New()
    k.Add("1.2.3.3")
    k.Add("2.4.5.6")
    k.Add("5.5.5.1")
    node, _ := k.Get("aaa")
    fmt.Printf("%v\n", node)
    k.Remove("2.4.5.6")
}

Skip list

Jump table is a kind of data structure which can search quickly by exchanging space for time. Thread safety is supported.

package main

import (
    "fmt"
    "github.com/liyue201/gostl/ds/skiplist"
)

func main()  {
    list := skiplist.New(skiplist.WithMaxLevel(15))
    list.Insert("aaa", "1111")
    list.Insert("bbb", "2222")
    fmt.Printf("aaa = %v\n", list.Get("aaa"))
    fmt.Printf("aaa = %v\n\n", list.Get("bbb"))

    list.Traversal(func(key, value interface{}) bool {
        fmt.Printf("key:%v value:%v\n", key, value)
        return true
    })

    list.Remove("aaa")
}

Sorting, stable sorting, binary search

Sort: fast sort algorithm is used internally.
Stable: stable sorting. Merge sorting is used internally.
BinarySearch: determine whether an element is in the scope of iterator through binary search.
LowerBound: through binary search, find the first data equal to the element and return the iterator.
UpperBound: find the first data larger than the element and return the iterator through binary search.

package main

import (
    "github.com/liyue201/gostl/algorithm/sort"
    "github.com/liyue201/gostl/utils/comparator"
    "github.com/liyue201/gostl/ds/slice"
    "fmt"
)

func main()  {
    a := make([]string, 0)
    a = append(a, "bbbb")
    a = append(a, "ccc")
    a = append(a, "aaaa")
    a = append(a, "bbbb")
    a = append(a, "bb")

    sliceA := slice.StringSlice(a)

    ////Sort in ascending order
    sort.Sort(sliceA.Begin(), sliceA.End())
    //sort.Stable(sliceA.Begin(), sliceA.End())
    fmt.Printf("%v\n", a)

    if sort.BinarySearch(sliceA.Begin(), sliceA.End(), "bbbb") {
        fmt.Printf("BinarySearch: found bbbb\n")
    }

    iter := sort.LowerBound(sliceA.Begin(), sliceA.End(), "bbbb")
    if iter.IsValid() {
        fmt.Printf("LowerBound bbbb: %v\n", iter.Value())
    }
    iter = sort.UpperBound(sliceA.Begin(), sliceA.End(), "bbbb")
    if iter.IsValid() {
        fmt.Printf("UpperBound bbbb: %v\n", iter.Value())
    }
    //Sort in descending order
    sort.Sort(sliceA.Begin(), sliceA.End(), comparator.Reverse(comparator.BuiltinTypeComparator))
    //sort.Stable(sliceA.Begin(), sliceA.End(), comparator.Reverse(comparator.BuiltinTypeComparator))
    fmt.Printf("%v\n", a)
}

Next [permutation]

This function modifies the data in the iterator range to the next sort combination.

package main

import (
    "github.com/liyue201/gostl/algorithm/sort"
    "github.com/liyue201/gostl/ds/slice"
    "github.com/liyue201/gostl/utils/comparator"
    "fmt"
)

func main()  {
    a := slice.IntSlice(make([]int, 0))

    for i := 1; i <= 3; i++ {
        a = append(a, i)
    }
    fmt.Println("NextPermutation")
    for {
        fmt.Printf("%v\n", a)
        if !sort.NextPermutation(a.Begin(), a.End()) {
            break
        }
    }
    fmt.Println("PrePermutation")
    for {
        fmt.Printf("%v\n", a)
        if !sort.NextPermutation(a.Begin(), a.End(), comparator.Reverse(comparator.BuiltinTypeComparator)) {
            break
        }
    }
}

Posted by beemzet on Tue, 05 Nov 2019 00:17:40 -0800