-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ismail Gjevori
committed
Apr 2, 2022
1 parent
55b23b1
commit c8eb8d3
Showing
2 changed files
with
311 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,152 @@ | ||
package collection | ||
|
||
// DLList is a doubly linked list. It is based on the linked list implementation. | ||
// It can be used as a stack and/or a queue. | ||
// All the operations are O(1), even when the size of the list is large. | ||
// | ||
// The zero value for DLList is an empty list ready to use. | ||
// var queue collection.DLList[int] | ||
// queue.PushBack(1) | ||
// queue.PushBack(2) | ||
// queue.PushBack(3) | ||
// fmt.Println(queue.PopFront()) // 1, true | ||
// fmt.Println(queue.PopFront()) // 2, true | ||
// fmt.Println(queue.PopFront()) // 3, true | ||
// fmt.Println(queue.PopFront()) // 0, false | ||
type DLList[T any] struct { | ||
head *node[T] | ||
tail *node[T] | ||
size int | ||
} | ||
|
||
// Back returns the last element of the list. | ||
// If the list is empty, the zero value is returned and false. | ||
func (ll *DLList[T]) Back() (T, bool) { | ||
if ll.size == 0 { | ||
return *new(T), false | ||
} | ||
return ll.tail.value, true | ||
} | ||
|
||
// Clear removes all elements from the list. | ||
func (ll *DLList[T]) Clear() { | ||
ll.head = nil | ||
ll.tail = nil | ||
ll.size = 0 | ||
} | ||
|
||
// Front returns the first element of the list. | ||
// If the list is empty, the zero value is returned and false. | ||
func (ll *DLList[T]) Front() (T, bool) { | ||
if ll.size == 0 { | ||
return *new(T), false | ||
} | ||
return ll.head.value, true | ||
} | ||
|
||
// IsEmpty returns true if the list is empty. | ||
func (ll *DLList[T]) IsEmpty() bool { | ||
return ll.size == 0 | ||
} | ||
|
||
// Iter returns a new iterator for the list, iterating the values from front to back. | ||
func (ll *DLList[T]) Iter() Iterator[T] { | ||
cur_node := ll.head | ||
return func() (T, bool) { | ||
if cur_node == nil { | ||
return *new(T), false | ||
} | ||
v := cur_node.value | ||
cur_node = cur_node.next | ||
return v, true | ||
} | ||
} | ||
|
||
// PopBack removes the last element from the list. | ||
// If the second return value is false, the list is empty and the zero value is returned. | ||
func (ll *DLList[T]) PopBack() (T, bool) { | ||
if ll.size == 0 { | ||
return *new(T), false | ||
} | ||
n := ll.tail | ||
if ll.size == 1 { | ||
ll.head = nil | ||
ll.tail = nil | ||
} else { | ||
ll.tail = n.prev | ||
ll.tail.next = nil | ||
} | ||
ll.size-- | ||
return n.value, true | ||
} | ||
|
||
// PopFront removes the first element from the list. | ||
// If the second return value is false, the list is empty and the zero value is returned. | ||
func (ll *DLList[T]) PopFront() (T, bool) { | ||
if ll.size == 0 { | ||
return *new(T), false | ||
} | ||
n := ll.head | ||
if ll.size == 1 { | ||
ll.head = nil | ||
ll.tail = nil | ||
} else { | ||
ll.head = n.next | ||
ll.head.prev = nil | ||
} | ||
ll.size-- | ||
return n.value, true | ||
} | ||
|
||
// PushBack adds a new element at the back of the list. | ||
func (ll *DLList[T]) PushBack(v T) { | ||
n := &node[T]{value: v} | ||
if ll.size == 0 { | ||
ll.head = n | ||
ll.tail = n | ||
} else { | ||
ll.tail.next = n | ||
n.prev = ll.tail | ||
ll.tail = n | ||
} | ||
ll.size++ | ||
} | ||
|
||
// PushFront adds a new element at the front of the list. | ||
func (ll *DLList[T]) PushFront(v T) { | ||
n := &node[T]{value: v} | ||
if ll.size == 0 { | ||
ll.head = n | ||
ll.tail = n | ||
} else { | ||
ll.head.prev = n | ||
n.next = ll.head | ||
ll.head = n | ||
} | ||
ll.size++ | ||
} | ||
|
||
// ReverseIter returns a new iterator for the list, iterating the values from back to front. | ||
func (ll *DLList[T]) ReverseIter() Iterator[T] { | ||
cur_node := ll.tail | ||
return func() (T, bool) { | ||
if cur_node == nil { | ||
return *new(T), false | ||
} | ||
v := cur_node.value | ||
cur_node = cur_node.prev | ||
return v, true | ||
} | ||
} | ||
|
||
// Size returns the number of elements in the list. | ||
func (ll *DLList[T]) Size() int { | ||
return ll.size | ||
} | ||
|
||
// node is a helper struct that holds the value and the links to the next and previous nodes. | ||
type node[T any] struct { | ||
value T | ||
prev *node[T] | ||
next *node[T] | ||
} |
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,159 @@ | ||
package collection | ||
|
||
import "testing" | ||
|
||
func TestBack(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushBack(1) | ||
queue.PushBack(2) | ||
queue.PushBack(3) | ||
s := queue.Size() | ||
if v, ok := queue.Back(); !ok || v != 3 { | ||
t.Errorf("queue.Back() = %v, %v, want %v, %v", v, ok, 3, true) | ||
} | ||
if s != queue.Size() { | ||
t.Errorf("queue.size cahnged was = %d, now = %d", s, queue.Size()) | ||
} | ||
} | ||
|
||
func TestClear(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushBack(1) | ||
queue.PushBack(2) | ||
queue.PushBack(3) | ||
queue.Clear() | ||
if !queue.IsEmpty() { | ||
t.Errorf("queue.IsEmpty() = %v, want %v", queue.IsEmpty(), true) | ||
} | ||
if queue.head != nil { | ||
t.Errorf("queue.head = %v, want %v", queue.head, nil) | ||
} | ||
if queue.tail != nil { | ||
t.Errorf("queue.tail = %v, want %v", queue.tail, nil) | ||
} | ||
} | ||
|
||
func TestFront(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushBack(1) | ||
queue.PushBack(2) | ||
queue.PushBack(3) | ||
s := queue.Size() | ||
if v, ok := queue.Front(); !ok || v != 1 { | ||
t.Errorf("queue.Front() = %v, %v, want %v, %v", v, ok, 1, true) | ||
} | ||
if s != queue.Size() { | ||
t.Errorf("queue.size cahnged was = %d, now = %d", s, queue.Size()) | ||
} | ||
} | ||
|
||
func TestIsEmpty(t *testing.T) { | ||
var queue DLList[int] | ||
if !queue.IsEmpty() { | ||
t.Errorf("queue.IsEmpty() = %v, want %v", queue.IsEmpty(), true) | ||
} | ||
queue.PushBack(1) | ||
if queue.IsEmpty() { | ||
t.Errorf("queue.IsEmpty() = %v, want %v", queue.IsEmpty(), false) | ||
} | ||
} | ||
|
||
func TestDLLIter(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushBack(1) | ||
queue.PushBack(2) | ||
queue.PushBack(3) | ||
var i int | ||
queue.Iter().ForEach(func(v int) { | ||
if v != i+1 { | ||
t.Errorf("queue.Iter().ForEach() = %v, want %v", v, i+1) | ||
} | ||
i++ | ||
}) | ||
if i != 3 { | ||
t.Errorf("queue.Iter().ForEach() = %v, want %v", i, 3) | ||
} | ||
i = 3 | ||
queue.ReverseIter().ForEach(func(v int) { | ||
if v != i { | ||
t.Errorf("queue.ReverseIter().ForEach() = %v, want %v", v, i) | ||
} | ||
i-- | ||
}) | ||
if i != 0 { | ||
t.Errorf("queue.ReverseIter().ForEach() = %v, want %v", i, 0) | ||
} | ||
} | ||
|
||
func TestPopBack(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushBack(1) | ||
queue.PushBack(2) | ||
queue.PushBack(3) | ||
s := queue.Size() | ||
if v, ok := queue.PopBack(); !ok || v != 3 { | ||
t.Errorf("queue.PopBack() = %v, %v, want %v, %v", v, ok, 3, true) | ||
} | ||
if s != queue.Size()+1 { | ||
t.Errorf("queue.size did not change, before = %d, now = %d", s, queue.Size()) | ||
} | ||
} | ||
|
||
func TestPopFront(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushBack(1) | ||
queue.PushBack(2) | ||
queue.PushBack(3) | ||
s := queue.Size() | ||
if v, ok := queue.PopFront(); !ok || v != 1 { | ||
t.Errorf("queue.PopFront() = %v, %v, want %v, %v", v, ok, 1, true) | ||
} | ||
if s != queue.Size()+1 { | ||
t.Errorf("queue.size did not change, before = %d, now = %d", s, queue.Size()) | ||
} | ||
} | ||
|
||
func TestPushBack(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushBack(1) | ||
queue.PushBack(2) | ||
queue.PushBack(3) | ||
s := queue.Size() | ||
if s != 3 { | ||
t.Errorf("queue.size = %d, want %d", s, 3) | ||
} | ||
if queue.head.value != 1 { | ||
t.Errorf("queue.head.value = %d, want %d", queue.head.value, 1) | ||
} | ||
if queue.tail.value != 3 { | ||
t.Errorf("queue.tail.value = %d, want %d", queue.tail.value, 3) | ||
} | ||
} | ||
|
||
func TestPushFront(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushFront(1) | ||
queue.PushFront(2) | ||
queue.PushFront(3) | ||
s := queue.Size() | ||
if s != 3 { | ||
t.Errorf("queue.size = %d, want %d", s, 3) | ||
} | ||
if queue.head.value != 3 { | ||
t.Errorf("queue.head.value = %d, want %d", queue.head.value, 3) | ||
} | ||
if queue.tail.value != 1 { | ||
t.Errorf("queue.tail.value = %d, want %d", queue.tail.value, 1) | ||
} | ||
} | ||
|
||
func TestSize(t *testing.T) { | ||
var queue DLList[int] | ||
queue.PushBack(1) | ||
queue.PushBack(2) | ||
queue.PushBack(3) | ||
s := queue.Size() | ||
if s != 3 { | ||
t.Errorf("queue.size = %d, want %d", s, 3) | ||
} | ||
} |