-
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.
Starting doubly-linked list implementation
- Loading branch information
Showing
2 changed files
with
138 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,100 @@ | ||
// Package list implements a doubly-linked list. | ||
package list | ||
|
||
import ( | ||
"fmt" | ||
"iter" | ||
) | ||
|
||
type Node[T any] struct { | ||
next, prev *Node[T] | ||
Value T | ||
} | ||
|
||
type List[T any] struct { | ||
front *Node[T] | ||
back *Node[T] | ||
length int | ||
} | ||
|
||
func New[T any]() *List[T] { | ||
// A List always has two allocated sentinel nodes: front points to the | ||
// first, and back points at the last. The length of the list remains | ||
// 0 as long as there are only sentinel nodes in it. | ||
lst := &List[T]{ | ||
front: &Node[T]{}, | ||
back: &Node[T]{}, | ||
length: 0, | ||
} | ||
lst.front.next = lst.back | ||
lst.back.prev = lst.front | ||
return lst | ||
} | ||
|
||
func (lst *List[T]) Len() int { | ||
return lst.length | ||
} | ||
|
||
func (lst *List[T]) Front() *Node[T] { | ||
if lst.length == 0 { | ||
return nil | ||
} | ||
return lst.front.next | ||
} | ||
|
||
func (lst *List[T]) Back() *Node[T] { | ||
if lst.length == 0 { | ||
return nil | ||
} | ||
return lst.back.prev | ||
} | ||
|
||
// InsertFront inserts a new node with the given value at the front of the list. | ||
func (lst *List[T]) InsertFront(val T) { | ||
oldFirst := lst.front.next | ||
lst.front.next = &Node[T]{ | ||
next: oldFirst, | ||
prev: lst.front, | ||
Value: val, | ||
} | ||
oldFirst.prev = lst.front.next | ||
lst.length += 1 | ||
} | ||
|
||
// InsertBack inserts a new node with the given value at the back of the list. | ||
func (lst *List[T]) InsertBack(val T) { | ||
oldLast := lst.back.prev | ||
lst.back.prev = &Node[T]{ | ||
next: lst.back, | ||
prev: oldLast, | ||
Value: val, | ||
} | ||
oldLast.next = lst.back.prev | ||
lst.length += 1 | ||
} | ||
|
||
// All returns an iterator over all the values in the list. | ||
func (lst *List[T]) All() iter.Seq[T] { | ||
return func(yield func(T) bool) { | ||
for node := lst.front.next; node != lst.back; node = node.next { | ||
if !yield(node.Value) { | ||
return | ||
} | ||
} | ||
} | ||
} | ||
|
||
func (lst *List[T]) debugPrint() { | ||
fmt.Println("-----------------------") | ||
for n := lst.front; n != nil; n = n.next { | ||
var specialName string | ||
if n == lst.front { | ||
specialName = " [FRONT]" | ||
} else if n == lst.back { | ||
specialName = " [BACK]" | ||
} | ||
fmt.Printf("| Node addr=%p%v:\n", n, specialName) | ||
fmt.Printf("| value=%v next=%p prev=%p\n", n.Value, n.next, n.prev) | ||
} | ||
fmt.Println("-----------------------") | ||
} |
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,38 @@ | ||
package list | ||
|
||
import ( | ||
"slices" | ||
"testing" | ||
) | ||
|
||
func checkList[T comparable](t *testing.T, lst *List[T], want []T) { | ||
t.Helper() | ||
if lst.Len() != len(want) { | ||
t.Errorf("got len=%v, want %v", lst.Len(), len(want)) | ||
} | ||
|
||
got := slices.Collect(lst.All()) | ||
if !slices.Equal(got, want) { | ||
t.Errorf("got %v, want %v", got, want) | ||
} | ||
} | ||
|
||
func TestBasicInsertFront(t *testing.T) { | ||
nl := New[int]() | ||
nl.InsertFront(20) | ||
checkList(t, nl, []int{20}) | ||
nl.InsertFront(30) | ||
checkList(t, nl, []int{30, 20}) | ||
nl.InsertFront(10) | ||
checkList(t, nl, []int{10, 30, 20}) | ||
} | ||
|
||
func TestBasicInsertBack(t *testing.T) { | ||
nl := New[int]() | ||
nl.InsertBack(20) | ||
checkList(t, nl, []int{20}) | ||
nl.InsertBack(30) | ||
checkList(t, nl, []int{20, 30}) | ||
nl.InsertBack(10) | ||
checkList(t, nl, []int{20, 30, 10}) | ||
} |