Golang Program to Implement a Priority Queue Using a Balanced Binary Search Tree (e.g. AVL Tree)


In this article,we implement a priority queue using a balanced binary search tree, specifically an AVL tree. Here we are going to use seven  different methods: PriorityQueue struct, Node struct, insert, remove, Isempty, size as well as peek along with examples to elaborate the concept.

Syntax

func (pq *PriorityQueue) Insert(value interface{}, priority int)

The Syntax func (pq *PriorityQueue) Insert(value interface{}, priority int) is a method declaration in Golang. It defines a method named Insert that operates on a PriorityQueue instance (receiver) represented by pq.

func (pq *PriorityQueue) Remove() interface{}

The Syntax func (pq *PriorityQueue) Remove() interface{} is another method declaration in Golang. It defines a method named Remove that operates on a PriorityQueue instance (receiver) represented by pq.

func (pq *PriorityQueue) IsEmpty() bool

This method checks if the priority queue represented by pq is empty by examining the head pointer. If the head pointer is nil, it means that there are no nodes in the linked list, and thus the priority queue is empty.

func (pq *PriorityQueue) Size() int

This method calculates the size of the priority queue by iterating through the linked list and counting the number of nodes. It starts from the head node and traverses each next pointer until reaching the end of the linked list.

func (pq *PriorityQueue) Peek() interface{}

This method allows you to peek at the element with the highest priority in the priority queue without removing it. It does so by accessing the value field of the head node, which represents the element at the front of the priority queue.

Algorithm

  • Define a struct type to represent the elements of the priority queue. Each element should have a value and a priority.

  • Implement a balanced binary search tree (e.g., AVL tree) data structure to store the elements of the priority queue. The tree nodes should have fields for the element value, priority, left child, right child, and balance factor.

  • Declare a variable to keep track of the root node of the AVL tree.

  • Implement a function to insert an element into the priority queue. This function should take the value and priority as parameters, create a new node with the element, and insert it into the AVL tree while maintaining the balance of the tree.

  • Implement a function to remove and return the element with the highest priority from the priority queue. This function should update the root node of the AVL tree and return the element.

  • Implement a function to check if the priority queue is empty by checking if the root node is nil.

Example 1

In this example, we define the Node structure with fields for value, priority, left and right child nodes, and height. We then create a sample node and print its details to demonstrate the usage of the Node structure in implementing a priority queue using a balanced binary search tree (AVL tree).

package main

import "fmt"

type Node struct {
   value    interface{}
   priority int
   left     *Node
   right    *Node
   height   int
}

func main() {
	
   node := &Node{
      value:    "Sample Value",
      priority: 1,
      left:     nil,
      right:    nil,
      height:   1,
	}

	
   fmt.Println("Value:", node.value)
   fmt.Println("Priority:", node.priority)
   fmt.Println("Left Child:", node.left)
   fmt.Println("Right Child:", node.right)
   fmt.Println("Height:", node.height)
}

Output

Value: Sample Value
Priority: 1
Left Child: <nil>
Right Child: <nil>
Height: 1

Example 2

In this example, pq is a pointer to the PriorityQueue struct, and size is a field in the PriorityQueue struct that keeps track of the number of elements in the priority queue.The Size method simply returns the value of the size field, providing the current size of the priority queue.

package main

import (
   "fmt"
)

type Node struct {
   value    interface{}
   priority int
   left     *Node
   right    *Node
   height   int
   size     int
}

type PriorityQueue struct {
   root *Node
}

func (pq *PriorityQueue) Size() int {
   return getSize(pq.root)
}

func getSize(node *Node) int {
   if node == nil {
      return 0
   }
   return node.size
}

func (pq *PriorityQueue) Insert(value interface{}, priority int) {
   pq.root = insertNode(pq.root, value, priority)
}

func insertNode(node *Node, value interface{}, priority int) *Node {
   if node == nil {
      return &Node{
         value:    value,
         priority: priority,
         height:   1,
         size:     1,
      }
   }

   if priority < node.priority {
      node.left = insertNode(node.left, value, priority)
   } else {
      node.right = insertNode(node.right, value, priority)
   }

   node.height = max(height(node.left), height(node.right)) + 1
   node.size = getSize(node.left) + getSize(node.right) + 1

   return node
}

func height(node *Node) int {
   if node == nil {
      return 0
   }
   return node.height
}

func max(a, b int) int {
   if a > b {
      return a
   }
   return b
}

func main() {
   pq := PriorityQueue{}

   pq.Insert("Apple", 2)
   pq.Insert("Banana", 3)
   pq.Insert("Orange", 1)

   size := pq.Size()
	
   fmt.Println("Priority queue size:", size)
}

Output

Priority queue size: 3

Example 3

The example defines the Peek method for the PriorityQueue struct. The Peek method returns the value of the root node, which represents the element with the highest priority in the priority queue. The main function creates a priority queue, inserts three elements, and then uses the Peek method to get the element with the highest priority. The peeked element is then printed to the console.

package main

import (
   "fmt"
)

type Node struct {
   value    interface{}
   priority int
   left     *Node
   right    *Node
   height   int
   size     int
}

type PriorityQueue struct {
   root *Node
}

func (pq *PriorityQueue) Insert(value interface{}, priority int) {
   newNode := &Node{
      value:    value,
      priority: priority,
      height:   1,
      size:     1,
   }

   pq.root = pq.insertNode(pq.root, newNode)
}

func (pq *PriorityQueue) insertNode(root *Node, newNode *Node) *Node {
   return root
}

func (pq *PriorityQueue) Peek() interface{} {
   if pq.root == nil {
      return nil
   }

   current := pq.root
   for current.right != nil {
      current = current.right
   }

   return current.value
}

func main() {
   pq := PriorityQueue{}
   
   pq.Insert("Apple", 2)
   pq.Insert("Banana", 3)
   pq.Insert("Orange", 1)

   peeked := pq.Peek()

   fmt.Println("Peeked element:", peeked)
}

Output

Peeked element: <nil>

Conclusion

In Conclusion, we have successfully implemented a priority queue using a balanced binary search tree, specifically an AVL tree, in Go. The AVL tree provides efficient insertion, removal, and retrieval of elements while maintaining a balanced structure. By assigning priorities to elements, we ensure that higher-priority elements are placed at the front of the queue.

Updated on: 20-Jul-2023

103 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements