diff --git a/datastructure/doublylink.go b/datastructure/doublylink.go new file mode 100644 index 0000000..144ea55 --- /dev/null +++ b/datastructure/doublylink.go @@ -0,0 +1,228 @@ +package datastructure + +import ( + "errors" + "fmt" +) + +// DoublyLink is a linked list. Whose node has a generic Value, Pre pointer points to a previous node of the link, Next pointer points to a next node of the link. +type DoublyLink[T any] struct { + Head *LinkNode[T] + length int +} + +// NewDoublyLink return *DoublyLink instance +func NewDoublyLink[T any]() *DoublyLink[T] { + return &DoublyLink[T]{Head: nil} +} + +// InsertAtHead insert value into doubly linklist at head index +func (link *DoublyLink[T]) InsertAtHead(value T) { + newNode := NewLinkNode(value) + size := link.Size() + + if size == 0 { + link.Head = newNode + link.length++ + return + } + + newNode.Next = link.Head + newNode.Pre = nil + + link.Head.Pre = newNode + link.Head = newNode + + link.length++ +} + +// InsertAtTail insert value into doubly linklist at tail index +func (link *DoublyLink[T]) InsertAtTail(value T) { + current := link.Head + if current == nil { + link.InsertAtHead(value) + return + } + + for current.Next != nil { + current = current.Next + } + + newNode := NewLinkNode(value) + newNode.Next = nil + newNode.Pre = current + current.Next = newNode + + link.length++ +} + +// InsertAt insert value into doubly linklist at index +func (link *DoublyLink[T]) InsertAt(index int, value T) error { + size := link.length + if index < 0 || index > size { + return errors.New("param index should between 0 and the length of doubly link.") + } + + if index == 0 { + link.InsertAtHead(value) + return nil + } + + if index == size { + link.InsertAtTail(value) + return nil + } + + i := 0 + current := link.Head + + for current != nil { + if i == index-1 { + newNode := NewLinkNode(value) + newNode.Next = current.Next + newNode.Pre = current + + current.Next = newNode + link.length++ + + return nil + } + i++ + current = current.Next + } + + return errors.New("doubly link list no exist") +} + +// DeleteAtHead delete value in doubly linklist at head index +func (link *DoublyLink[T]) DeleteAtHead() error { + if link.Head == nil { + return errors.New("doubly link list no exist") + } + current := link.Head + link.Head = current.Next + link.Head.Pre = nil + link.length-- + + return nil +} + +// DeleteAtTail delete value in doubly linklist at tail index +func (link *DoublyLink[T]) DeleteAtTail() error { + if link.Head == nil { + return errors.New("doubly link list no exist") + } + current := link.Head + if current.Next == nil { + return link.DeleteAtHead() + } + + for current.Next.Next != nil { + current = current.Next + } + + current.Next = nil + link.length-- + return nil +} + +// DeleteAt delete value in doubly linklist at index +func (link *DoublyLink[T]) DeleteAt(index int) error { + if link.Head == nil { + return errors.New("doubly link list no exist") + } + current := link.Head + if current.Next == nil || index == 0 { + return link.DeleteAtHead() + } + + if index == link.length-1 { + return link.DeleteAtTail() + } + + if index < 0 || index > link.length-1 { + return errors.New("param index should between 0 and link size -1.") + } + + i := 0 + for current != nil { + if i == index-1 { + current.Next = current.Next.Next + link.length-- + return nil + } + i++ + current = current.Next + } + + return errors.New("delete error") +} + +// Reverse the linked list +func (link *DoublyLink[T]) Reverse() { + current := link.Head + var temp *LinkNode[T] + + for current != nil { + temp = current.Pre + current.Pre = current.Next + current.Next = temp + current = current.Pre + } + + if temp != nil { + link.Head = temp.Pre + } +} + +// GetMiddleNode return node at middle index of linked list +func (link *DoublyLink[T]) GetMiddleNode() *LinkNode[T] { + if link.Head == nil { + return nil + } + if link.Head.Next == nil { + return link.Head + } + fast := link.Head + slow := link.Head + + for fast != nil { + fast = fast.Next + + if fast != nil { + fast = fast.Next + slow = slow.Next + } else { + return slow + } + } + return slow +} + +// Size return the count of doubly linked list +func (link *DoublyLink[T]) Size() int { + return link.length +} + +// Values return slice of all doubly linklist node value +func (link *DoublyLink[T]) Values() []T { + res := []T{} + current := link.Head + for current != nil { + res = append(res, current.Value) + current = current.Next + } + return res +} + +// Print all nodes info of a linked list +func (link *DoublyLink[T]) Print() { + current := link.Head + info := "[ " + for current != nil { + info += fmt.Sprintf("%+v, ", current) + current = current.Next + } + info += " ]" + fmt.Println(info) +} diff --git a/datastructure/doublylink_test.go b/datastructure/doublylink_test.go new file mode 100644 index 0000000..7518966 --- /dev/null +++ b/datastructure/doublylink_test.go @@ -0,0 +1,165 @@ +package datastructure + +import ( + "testing" + + "github.com/duke-git/lancet/internal" +) + +func TestDoublyLink_InsertAtFirst(t *testing.T) { + assert := internal.NewAssert(t, "TestDoublyLink_InsertAtFirst") + + link := NewDoublyLink[int]() + link.InsertAtHead(1) + link.InsertAtHead(2) + link.InsertAtHead(3) + link.Print() + + expected := []int{3, 2, 1} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_InsertAtTail(t *testing.T) { + assert := internal.NewAssert(t, "TestDoublyLink_InsertAtTail") + + link := NewDoublyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.Print() + + expected := []int{1, 2, 3} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_InsertAt(t *testing.T) { + assert := internal.NewAssert(t, "TestDoublyLink_InsertAt") + + link := NewDoublyLink[int]() + + err := link.InsertAt(1, 1) + assert.IsNotNil(err) + + err = link.InsertAt(0, 1) + err = link.InsertAt(1, 2) + err = link.InsertAt(2, 4) + err = link.InsertAt(2, 3) + + if err != nil { + t.FailNow() + } + link.Print() + + expected := []int{1, 2, 3, 4} + values := link.Values() + + assert.Equal(expected, values) + +} + +func TestDoublyLink_DeleteAtHead(t *testing.T) { + assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtHead") + + link := NewDoublyLink[int]() + err := link.DeleteAtHead() + assert.IsNotNil(err) + + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.DeleteAtHead() + link.Print() + + expected := []int{2, 3, 4} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_DeleteAtTail(t *testing.T) { + assert := internal.NewAssert(t, "TestDoublyLink_DeleteAtTail") + + link := NewDoublyLink[int]() + err := link.DeleteAtTail() + assert.IsNotNil(err) + + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.DeleteAtTail() + link.Print() + + expected := []int{1, 2, 3} + values := link.Values() + + assert.Equal(expected, values) +} + +func TestDoublyLink_DeleteAt(t *testing.T) { + assert := internal.NewAssert(t, "TestDoublyLink_DeleteAt") + + link := NewDoublyLink[int]() + err := link.DeleteAt(0) + assert.IsNotNil(err) + + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + link.InsertAtTail(5) + + err = link.DeleteAt(5) + assert.IsNotNil(err) + + err = link.DeleteAt(0) + assert.IsNil(err) + assert.Equal([]int{2, 3, 4, 5}, link.Values()) + + link.DeleteAt(3) + assert.Equal([]int{2, 3, 4}, link.Values()) + + link.DeleteAt(1) + assert.Equal(2, link.Size()) + assert.Equal([]int{2, 4}, link.Values()) +} + +func TestDoublyLink_Reverse(t *testing.T) { + assert := internal.NewAssert(t, "TestDoublyLink_Reverse") + + link := NewDoublyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + link.Reverse() + link.Print() + assert.Equal([]int{4, 3, 2, 1}, link.Values()) +} + +func TestDoublyLink_GetMiddleNode(t *testing.T) { + assert := internal.NewAssert(t, "TestDoublyLink_GetMiddleNode") + + link := NewDoublyLink[int]() + link.InsertAtTail(1) + link.InsertAtTail(2) + link.InsertAtTail(3) + link.InsertAtTail(4) + + middle1 := link.GetMiddleNode() + assert.Equal(3, middle1.Value) + + link.InsertAtTail(5) + link.InsertAtTail(6) + link.InsertAtTail(7) + middle2 := link.GetMiddleNode() + assert.Equal(4, middle2.Value) +}