Skip to content

Commit

Permalink
Merge 59a1e69 into c4ca3ae
Browse files Browse the repository at this point in the history
  • Loading branch information
chen3feng authored Sep 10, 2022
2 parents c4ca3ae + 59a1e69 commit db57ff7
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 146 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,15 @@ so C++ users may feel familiar, and sometimes (maybe) feel more convenient.

### Containers

Currently implemented containers are:
There are following container interfaces:

- `Container` is the base interface for all containers
- `Map` is a key-value associative map
- `Set` is set
- `Queue` is a FIFO Queue
- `Deque` is a double ended queue

Currently container implementations are:

- [x] `BuiltinSet` provided a set funtionality based on Go's own `map`. It provides basic operations such as insert,
search and remove, as well as advanced functions such as union, intersection, difference, subset, superset, and disjoint.
Expand All @@ -53,7 +61,7 @@ Currently implemented containers are:
- [x] [SkipList](skiplist.md) is an ordered associative container that fills the gap where Go `map` only supports unordered.
This is currently the fastest skip list I tested in GitHub, see [skiplist-survey](https://github.com/chen3feng/skiplist-survey) for performance comparison
- [x] `Stack`, is a FILO container based on Slice implementation
- [x] `Queue` is a bidirectional FIFO queue, implemented based on linked list.
- [x] `DListQueue` is a bidirectional FIFO queue, implemented based on linked list.
- [x] `PriorityQuque` is a priority queue based on heap. Much easier to use and faster than [container/heap](https://pkg.go.dev/container/heap).

Different containers support different methods. The following are the methods supported by all containers:
Expand Down
14 changes: 11 additions & 3 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,23 @@ import "github.com/chen3feng/stl4go"

### 容器

目前实现的容器有
定义了如下容器接口

- [x] `Set` 集合。用 Go 自己的 map 封装了一个 `BuiltinSet`,提供了插入查找删除等基本操作,以及并集、交集、差集、子集、超集、不交集等高级功能。
- `Container` 是所有容器的基础接口
- `Map` 定义了 key-value 关联容器
- `Set` 定义了集合容器的接口
- `Queue` 定义了先进先出的队列的接口
- `Deque` 定义了双端队列的接口

提供的具体容器实现有:

- [x] `BuiltinSet` 集合。基于 Go 自己的 map 封装,提供了插入查找删除等基本操作,以及并集、交集、差集、子集、超集、不交集等高级功能。
- [x] `Vector` 是基于 slice 封装的向量。提供了中间插入删除、区间删除等功能,依然与 slice 兼容。
- [x] `DList` 是双链表容器,支持两端插入删除。
- [x] `SList` 是单链表容器,支持头部插入删除及尾部插入。
- [x] [跳表(SkipList)](skiplist.md) 是一种有序的关联容器,可以填补 Go `map` 只支持无序的的空白。这是目前全 GitHub 最快的跳表,参见 [skiplist-survey](https://github.com/chen3feng/skiplist-survey)的性能比较
- [x] `Stack`,栈基于 Slice 实现
- [x] `Queue` 双向进出的队列,基于链表实现
- [x] `DListQueue` 双向进出的队列,基于双链表实现
- [x] `PriorityQuque` 优先队列,基于堆实现,比 [container/heap](https://pkg.go.dev/container/heap) 更易用而且快不少。

不同的容器支持的方法不同,下面是所有容器都支持的方法:
Expand Down
23 changes: 23 additions & 0 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,26 @@ type Set[K any] interface {
ForEach(func(K)) // Iterate the container.
ForEachIf(func(K) bool) // Iterate the container, stops when the callback returns false.
}

// Queue is a container that can add elements to one end and remove elements from the other end.
type Queue[T any] interface {
Container
Front()
Back()
Push(T)
Pop() T
TryPop() (T, bool)
}

// Deque is a container that can add and remove elements from both ends.
type Deque[T any] interface {
Container
Front() T
Back() T
PushFront(T)
PushBack(T)
PopFront() T
PopBack() T
TryPopFront() (T, bool)
TryPopBack() (T, bool)
}
19 changes: 19 additions & 0 deletions dlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ func (l *DList[T]) Iterate() MutableIterator[T] {
return &dlistIterator[T]{l, node}
}

// Front returns the first element in the container.
func (l *DList[T]) Front() T {
if l.IsEmpty() {
panic("!IsEmpty")
}
return l.head.next.value
}

// Back returns the last element in the container.
func (l *DList[T]) Back() T {
if l.IsEmpty() {
panic("!IsEmpty")
}
return l.head.prev.value
}

// PushFront pushes an element at the front of the list.
func (l *DList[T]) PushFront(val T) {
l.ensureHead()
Expand Down Expand Up @@ -146,6 +162,9 @@ func (l *DList[T]) TryPopBack() (T, bool) {

// ForEach iterate the list, apply each element to the cb callback function.
func (l *DList[T]) ForEach(cb func(val T)) {
if l.head == nil {
return
}
for n := l.head.next; n != l.head; n = n.next {
cb(n.value)
}
Expand Down
40 changes: 25 additions & 15 deletions queue.go → dlist_queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,73 @@ import (
"fmt"
)

// Queue is a FIFO container
type Queue[T any] struct {
// DListQueue is a FIFO container
type DListQueue[T any] struct {
list DList[T]
}

// NewQueue create a new Queue object.
func NewQueue[T any]() *Queue[T] {
q := Queue[T]{}
// NewDListQueue create a new Queue object.
func NewDListQueue[T any]() *DListQueue[T] {
q := DListQueue[T]{}
return &q
}

// Len implements the Container interface.
func (q *Queue[T]) Len() int {
func (q *DListQueue[T]) Len() int {
return q.list.Len()
}

// IsEmpty implements the Container interface.
func (q *Queue[T]) IsEmpty() bool {
func (q *DListQueue[T]) IsEmpty() bool {
return q.list.IsEmpty()
}

// Clear implements the Container interface.
func (q *Queue[T]) Clear() {
func (q *DListQueue[T]) Clear() {
q.list.Clear()
}

// Len implements the fmt.Stringer interface.
func (q *Queue[T]) String() string {
func (q *DListQueue[T]) String() string {
return fmt.Sprintf("Queue[%v]", nameOfType[T]())
}

// Front returns the first element in the container.
func (q *DListQueue[T]) Front() T {
return q.list.Front()
}

// Back returns the last element in the container.
func (q *DListQueue[T]) Back() T {
return q.list.Back()
}

// PushFront pushed an element to the front of the queue.
func (q *Queue[T]) PushFront(val T) {
func (q *DListQueue[T]) PushFront(val T) {
q.list.PushFront(val)
}

// PushBack pushed an element to the back of the queue.
func (q *Queue[T]) PushBack(val T) {
func (q *DListQueue[T]) PushBack(val T) {
q.list.PushBack(val)
}

// PopFront popups an element from the front of the queue.
func (q *Queue[T]) PopFront() T {
func (q *DListQueue[T]) PopFront() T {
return q.list.PopFront()
}

// PopBack popups an element from the back of the queue.
func (q *Queue[T]) PopBack() T {
func (q *DListQueue[T]) PopBack() T {
return q.list.PopBack()
}

// TryPopFront tries popuping an element from the front of the queue.
func (q *Queue[T]) TryPopFront() (T, bool) {
func (q *DListQueue[T]) TryPopFront() (T, bool) {
return q.list.TryPopFront()
}

// TryPopBack tries popuping an element from the back of the queue.
func (q *Queue[T]) TryPopBack() (T, bool) {
func (q *DListQueue[T]) TryPopBack() (T, bool) {
return q.list.TryPopBack()
}
35 changes: 23 additions & 12 deletions queue_test.go → dlist_queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,86 @@ import (
)

func Test_Queue_Interface(t *testing.T) {
_ = Container(NewQueue[int]())
q := NewDListQueue[int]()
_ = Deque[int](q)
}

func Test_Queue_New(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
expectTrue(t, q.IsEmpty())
expectEq(t, q.Len(), 0)
}

func Test_Queue_Clear(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
q.PushBack(1)
q.Clear()
expectTrue(t, q.IsEmpty())
expectEq(t, q.Len(), 0)
}

func Test_Queue_String(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
expectEq(t, q.String(), "Queue[int]")
}

func Test_Queue_Front_Back(t *testing.T) {
q := NewDListQueue[int]()
expectPanic(t, func() { q.Front() })
expectPanic(t, func() { q.Back() })
q.PushBack(1)
q.PushBack(2)
expectEq(t, q.Front(), 1)
expectEq(t, q.Back(), 2)
}

func Test_Queue_PushFront(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
q.PushFront(1)
expectFalse(t, q.IsEmpty())
expectEq(t, q.Len(), 1)
}

func Test_Queue_PushBack(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
q.PushBack(1)
expectFalse(t, q.IsEmpty())
expectEq(t, q.Len(), 1)
}

func Test_Queue_TryPopFront(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
_, ok := q.TryPopFront()
expectFalse(t, ok)
}
func Test_Queue_TryPopBack(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
_, ok := q.TryPopBack()
expectFalse(t, ok)
}

func Test_Queue_PushFront_PopFront(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
q.PushFront(1)
q.PushFront(2)
expectEq(t, q.PopFront(), 2)
expectEq(t, q.PopFront(), 1)
}

func Test_Queue_PushFront_PopBack(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
q.PushFront(1)
expectEq(t, q.PopBack(), 1)
}

func Test_Queue_PushBack_PopFront(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
q.PushBack(1)
expectEq(t, q.PopFront(), 1)
}

func Test_Queue_PushBack_PopBack(t *testing.T) {
q := NewQueue[int]()
q := NewDListQueue[int]()
q.PushBack(1)
expectEq(t, q.PopBack(), 1)
}
16 changes: 15 additions & 1 deletion dlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func Test_DList_New(t *testing.T) {
expectEq(t, l.Len(), 0)
}

func Test_DList_NewOf(t *testing.T) {
func Test_DListOf(t *testing.T) {
l := DListOf(1, 2, 3)
expectFalse(t, l.IsEmpty())
expectEq(t, l.Len(), 3)
Expand Down Expand Up @@ -45,6 +45,12 @@ func Test_DList_Iterate_Empty(t *testing.T) {
expectEq(t, i, 0)
}

func Test_DList_FrontBack(t *testing.T) {
l := DListOf(1, 2, 3)
expectEq(t, l.Front(), 1)
expectEq(t, l.Back(), 3)
}

func Test_DList_PushFront(t *testing.T) {
l := DList[int]{}
l.PushFront(1)
Expand Down Expand Up @@ -156,6 +162,14 @@ func Test_DList_ForEachMutableIf(t *testing.T) {
expectEq(t, c, 2)
}

func Test_DList_ForEach_EmptyOK(t *testing.T) {
l := DList[int]{}
l.ForEach(func(n int) {})
l.ForEachIf(func(n int) bool { return true })
l.ForEachMutable(func(n *int) {})
l.ForEachMutableIf(func(n *int) bool { return true })
}

func Benchmark_DList_Iterate(b *testing.B) {
l := DListOf(Range(1, 10000)...)
b.Run("Iterator", func(b *testing.B) {
Expand Down
Loading

0 comments on commit db57ff7

Please sign in to comment.