Golang program to implement a deque using doubly linked list


A deque is a versatile data structure that allows insertion and deletion of elements from both ends efficiently. The doubly linked list provides an excellent foundation for building a deque as it allows easy traversal in both directions. In this article, we will explore deque using doubly linked lists in go with two methods: using a custom doubly linked list and using the built-in container/list package in Golang. Here in the below examples we will showcase the operations of a double ended queue, we will perform the insertion and deletion operations at both the ends.

Explanation

As you can see in the below diagram the deque will start from the front with null values and ends at rear with null values. The elements are connected in the doubly linked list manner in between the front and rear. We can see that each node has a value and are connected with the previous and next node, which makes the insertion and deletion of elements easier.

     Front                                 Rear
       ↓                                     ↓
+------+------+    +------+------+    +------+------+
| Prev | Next |  ↔ | Prev | Next |  ↔ | Prev | Next |
| Null | Node +─┐  | Node | Node +─┐  | Node | Null |
|      |      │  |        |      │    |      |      |
+------+------+  |  +------+------+   |+------+------+  
               └─►| Prev | Next |  |  | Prev | Next |
                  | Node | Null |  |  | Node | Node |
                  |      |      |  |  |      |      |
                  +------+------+  |  +------+------+
                                 └─►| Prev | Next |
                                    | Null | Node |
                                    |      |      |
                                    +------+------+

Syntax

newNode := &Node{value: value, prev: nil, next: nil}

The syntax newNode := &Node{value: value, prev: nil, next: nil} creates a new instance of a struct named Node with three fields: value, prev, and next. The “&” symbol is used to take the address of the newly created struct, making newNode a pointer to the struct. The value field is initialised with the value provided in the variable value, and the prev and next fields are initialised with nil, indicating that they are initially empty.

deque := list.New()

The syntax uses the container/list package provided by Go's standard library to implement a deque. The list.New() function initialises a new doubly linked list, which serves as the deque.

Algorithm

  • Create a "Node" struct with "value", "prev", and "next" fields to represent the nodes in the doubly linked list. Create a "Deque" struct with "front" and "rear" pointers initialized as nil.

  • Implement the "PushFront(value)" operation to insert a new node with the given value at the front of the deque.

  • Implement the "PushBack(value)" operation to insert a new node with the given value at the rear of the deque.

  • Implement the "PopFront()" operation to remove and return the value of the front node. Implement the "PopBack()" operation to remove and return the value of the rear node.

  • Implement the "Front()" operation to get the value of the element at the front of the deque.Implement the "Back()" operation to get the value of the element at the rear of the deque.

Example 1

In this example, we create a custom data structure to implement deque using doubly linked list. We define a Node struct with value, next, and prev pointers to represent each element in the deque. The Deque struct consists of front and rear pointers. We provide methods to insert elements at the front and rear, delete elements from the front and rear, and display the contents of the deque. The unbounded linked list allows dynamic resizing to accommodate any number of elements.

package main

import (
	"fmt"
)

type Node struct {
	value      interface{}
	prev, next *Node
}

type Deque struct {
	front, rear *Node
}

func (d *Deque) PushFront(value interface{}) {
	newNode := &Node{value: value}
	if d.front == nil {
		d.front, d.rear = newNode, newNode
	} else {
		newNode.next = d.front
		d.front.prev = newNode
		d.front = newNode
	}
}

func (d *Deque) PushBack(value interface{}) {
	newNode := &Node{value: value}
	if d.rear == nil {
		d.front, d.rear = newNode, newNode
	} else {
		newNode.prev = d.rear
		d.rear.next = newNode
		d.rear = newNode
	}
}

func (d *Deque) PopFront() interface{} {
	if d.front == nil {
		return nil
	}
	value := d.front.value
	d.front = d.front.next
	if d.front != nil {
		d.front.prev = nil
	} else {
		d.rear = nil
	}
	return value
}

func (d *Deque) PopBack() interface{} {
	if d.rear == nil {
		return nil
	}
	value := d.rear.value
	d.rear = d.rear.prev
	if d.rear != nil {
		d.rear.next = nil
	} else {
		d.front = nil
	}
	return value
}

func main() {
	deque := &Deque{}

	deque.PushBack(10)
	deque.PushFront(5)
	deque.PushBack(20)

	fmt.Println("Deque after adding elements:", deque)

	fmt.Println("Front element:", deque.PopFront())
	fmt.Println("Rear element:", deque.PopBack())

	fmt.Println("Deque after removing elements:", deque)
}

Output

Deque after adding elements: &{0xc00009c040 0xc00009c060}
Front element: 5
Rear element: 20
Deque after removing elements: &{0xc00009c020 0xc00009c020}

Example 2

In this example,to implement deque using doubly linked list we use the container/list package provided by Go's standard library to implement a deque. We start by creating a new doubly linked list named "deque" using list.New(). We then insert elements at the front and rear of the deque using the PushFront() and PushBack() functions, respectively. The Front() and Back() functions are used to access the elements at the front and rear of the deque. After removing elements from the front and rear of the deque using the Remove() function, we display the updated front and rear elements.

package main

import (
	"container/list"
	"fmt"
)

func main() {
	deque := list.New()

	deque.PushFront(3)
	deque.PushFront(2)
	deque.PushFront(1)

	deque.PushBack(4)
	deque.PushBack(5)

	fmt.Println("Front of the Deque:", deque.Front().Value)
	fmt.Println("Rear of the Deque:", deque.Back().Value)

	deque.Remove(deque.Front())
	deque.Remove(deque.Back())

	fmt.Println("Front of the Deque after removal:", deque.Front().Value)
	fmt.Println("Rear of the Deque after removal:", deque.Back().Value)
}

Output

Front of the Deque: 1
Rear of the Deque: 5
Front of the Deque after removal: 2
Rear of the Deque after removal: 4

Real-life implementations

Task Prioritization

In task scheduling systems, a deque can be employed to manage tasks with varying priorities. New tasks can be added to the rear of the deque, and high-priority tasks can be added or moved to the front. This ensures efficient execution of critical tasks while maintaining a list of pending tasks.

Sliding Window Problems

When dealing with streaming data or sequences, a deque can help solve sliding window problems efficiently. It allows constant-time addition and removal of elements from both ends, making it ideal for tasks like finding maximum or minimum values within a moving window of data.

Conclusion

Deques enable efficient insertion and removal from both ends, making them versatile data structures. In this article we have looked at how to implement deque using doubly linked list in go. Here we have seen two methods, in the first example we create a custom doubly linked list and in the second example we have used the built-in container/list package to perform the operation.

Updated on: 05-Sep-2023

140 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements