Data Structures: 4. Doubly Linked List
Mar 26, 2022
Introduction #
A doubly linked list is the same as a linked list, execept that now each item is linked to both the next one and the previous one. So if you have a linked list with the following items (1, 2, 3, 4) it would look like this:
flowchart LR; H(["HEAD"]) --> A([1]); A([1]) <--> B([2]); B([2]) <--> C([3]); C([3]) <--> D([4]); D([4]) --> E(["NIL"])
The operations available on a doubly linked list can vary - in this case we provide a function to check to see if the doubly linked list is empty or not, a function to get the length of the list, a function to get the items without removing them, a function to list the items without removing them, a function to search the doubly linked list, as well as functions to add or remove items from the front of the list, the back of the list, as well as a specified position in the list.
Solution #
package main
import "fmt"
type Node struct {
data any
prev *Node
next *Node
}
type DoublyLinkedList struct {
head *Node
}
func (l *DoublyLinkedList) IsEmpty() bool {
if l.head == nil {
return true
}
return false
}
func (l *DoublyLinkedList) Length() int {
count := 0
if l.head == nil {
return count
}
current := l.head
count += 1
for current.next != nil {
count += 1
current = current.next
}
return count
}
func (l *DoublyLinkedList) List() {
tmp := []any{}
current := l.head
tmp = append(tmp, current.data)
for current.next != nil {
current = current.next
tmp = append(tmp, current.data)
}
fmt.Printf("Nodes: %+v\n", tmp)
tmp = []any{}
tmp = append(tmp, current.data)
for current.prev != nil {
current = current.prev
tmp = append(tmp, current.data)
}
fmt.Printf("Nodes in Reverse: %+v\n", tmp)
}
func (l *DoublyLinkedList) Get() []any {
tmp := []any{}
current := l.head
tmp = append(tmp, current.data)
for current.next != nil {
current = current.next
tmp = append(tmp, current.data)
}
return tmp
}
func (l *DoublyLinkedList) Search(term any) any {
current := l.head
if current.data == term {
return current.data
}
for current.next != nil {
current = current.next
if current.data == term {
return current.data
}
}
return nil
}
func (l *DoublyLinkedList) Push(value any) {
n := &Node{}
n.data = value
n.next = nil
n.prev = nil
current := &Node{}
if l.head == nil {
l.head = n
return
} else {
current = l.head
for current.next != nil {
current = current.next
}
}
current.next = n
n.prev = current
}
func (l *DoublyLinkedList) PushAt(index int, value any) {
at := 0
n := &Node{}
n.data = value
n.next = nil
n.prev = nil
current := &Node{}
if l.head == nil {
l.head = n
n.prev = l.head
return
} else {
current = l.head
for current.next != nil {
at++
if index == at {
if current.next == nil {
l.Push(value)
return
} else {
n.next = current.next
n.next.prev = n
n.prev = current
current.next = n
}
break
}
current = current.next
}
return
}
fmt.Println("Index out of range")
}
func (l *DoublyLinkedList) PushBack(value any) {
n := &Node{}
n.data = value
n.next = nil
n.prev = nil
if l.head == nil {
l.head = n
return
} else {
n.next = l.head
l.head.prev = n
l.head = n
}
}
func (l *DoublyLinkedList) Pop() {
current := l.head
for current.next != nil {
if current.next.next != nil {
current = current.next
current.next.prev = current
continue
}
break
}
current.next = nil
}
func (l *DoublyLinkedList) PopAt(index int) {
at := 0
current := l.head
if index == 0 {
l.PopBack()
return
}
for current.next != nil {
at++
if at == index {
if current.next != nil {
if current.next.next != nil {
current.next.next.prev = current
}
current.next = current.next.next
break
}
}
current = current.next
}
}
func (l *DoublyLinkedList) PopBack() {
if l.head != nil {
if l.head.next == nil {
l.head = nil
} else {
l.head.next.prev = nil
l.head = l.head.next
}
} else {
fmt.Println("Linked list is empty")
}
}
func main() {
fmt.Println("Create a new linked list")
l := &DoublyLinkedList{}
fmt.Printf("Length of list: %d\n", l.Length())
fmt.Printf("List is empty?: %v\n", l.IsEmpty())
fmt.Println("Push seven items into the list")
fmt.Println("('One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven')")
l.Push("One")
l.Push("Two")
l.Push("Three")
l.Push("Four")
l.Push("Five")
l.Push("Six")
l.Push("Seven")
fmt.Println("Display the list")
l.List()
fmt.Printf("Length of list: %d\n", l.Length())
fmt.Printf("List is empty?: %v\n", l.IsEmpty())
fmt.Println("Search the list")
fmt.Printf("Find 'One': %v\n", l.Search("One"))
fmt.Printf("Find 'Seven': %v\n", l.Search("Seven"))
fmt.Printf("Find 'Ten': %v\n", l.Search("Ten"))
fmt.Println("Test getting the entire list")
fmt.Printf("List = %+v\n", l.Get())
fmt.Println("Pop the last item from the list")
l.Pop()
l.List()
fmt.Println("Pop the first item from the list")
l.PopBack()
l.List()
fmt.Println("Pop the third item from the list (first item is item 0)")
l.PopAt(2)
l.List()
fmt.Println("Pop the fourth item from the list")
l.PopAt(3)
l.List()
fmt.Println("Pop the first item from the list")
l.PopAt(0)
l.List()
fmt.Println("Push an item (1) at the start of the list")
l.PushBack(1)
l.List()
fmt.Println("Push an item (2) into the list at position 1 in the list")
l.PushAt(1, 2)
l.List()
fmt.Println("Push an item (4) into the list at position 3 in the list")
l.PushAt(3, 4)
l.List()
fmt.Println("Push an item (6) at the end of the list")
l.Push(6)
l.List()
fmt.Println("Trying searching for an integer item")
fmt.Printf("Find 4: %v\n", l.Search(4))
}
Testing #
Create a new linked list
Length of list: 0
List is empty?: true
Push seven items into the list
('One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven')
Display the list
Nodes: [One Two Three Four Five Six Seven]
Nodes in Reverse: [Seven Six Five Four Three Two One]
Length of list: 7
List is empty?: false
Search the list
Find 'One': One
Find 'Seven': Seven
Find 'Ten': <nil>
Test getting the entire list
List = [One Two Three Four Five Six Seven]
Pop the last item from the list
Nodes: [One Two Three Four Five Six]
Nodes in Reverse: [Six Five Four Three Two One]
Pop the first item from the list
Nodes: [Two Three Four Five Six]
Nodes in Reverse: [Six Five Four Three Two]
Pop the third item from the list (first item is item 0)
Nodes: [Two Three Five Six]
Nodes in Reverse: [Six Five Three Two]
Pop the fourth item from the list
Nodes: [Two Three Five]
Nodes in Reverse: [Five Three Two]
Pop the first item from the list
Nodes: [Three Five]
Nodes in Reverse: [Five Three]
Push an item (1) at the start of the list
Nodes: [1 Three Five]
Nodes in Reverse: [Five Three 1]
Push an item (2) into the list at position 1 in the list
Nodes: [1 2 Three Five]
Nodes in Reverse: [Five Three 2 1]
Push an item (4) into the list at position 3 in the list
Nodes: [1 2 Three 4 Five]
Nodes in Reverse: [Five 4 Three 2 1]
Push an item (6) at the end of the list
Nodes: [1 2 Three 4 Five 6]
Nodes in Reverse: [6 Five 4 Three 2 1]
Trying searching for an integer item
Find 4: 4