Priority Queue

Implementation of Priority Queue using Heap


Category: Data Structure And Algorithms Tags: C#

Priority Queue Code Files

Introduction

    Unlike ordinary queues, a priority queue allows to insert an object with priority so the highest priority objects can be drawn first in FIFO (First in first out) manner. Once objects with highest priority are fetched, objects with second highest priority can be fetched.

There may be many ways to implement priority queues but most efficient one is using heaps. Insertion can be done in heaps in log n time and we will do Enqueue and Dequeue operations here in log n time.

Insertion in Max-Heap
Fig 1: Insertion in Max-Heap


We can see above maximum element will always be on top. When we remove first element from heap (which is root i.e. 9 in above figure) we will place the last element to its position and move elements accordingly to maintain maximum heap as shown below:

Deletion in Max-Heap
Fig 2: Deletion in Max-Heap

Implementation

    Create a generic class "PriorityQueue" as below:

public class PriorityQueue<T>
{
    class Node
    {
        public int Priority { get; set; }
        public T Object { get; set; }
    }

    //object array
    List<Node> queue = new List<Node>();
    int heapSize = -1;
    bool _isMinPriorityQueue;
    public int Count { get { return queue.Count; } }

    /// <summary>
    /// If min queue or max queue
    /// </summary>
    /// <param name="isMinPriorityQueue"></param>
    public PriorityQueue(bool isMinPriorityQueue = false)
    {
        _isMinPriorityQueue = isMinPriorityQueue;
    }


    public void Enqueue(int priority, T obj){...}
    public T Dequeue(){...}
    public void UpdatePriority(T obj, int priority){...}
    public bool IsInQueue(T obj){...}

    private void BuildHeapMax(int i){...}
    private void BuildHeapMin(int i){...}
    private void MaxHeapify(int i){...}
    private void MinHeapify(int i){...}
private void Swap(int i, int j) { var temp = queue[i]; queue[i] = queue[j]; queue[j] = temp; } private int ChildL(int i) { return i * 2 + 1; } private int ChildR(int i) { return i * 2 + 2; } }

MaxHeapify and MinHeapify methods we have learned in previous article heap sort. We know these functions are required to maintain max and min heaps. We will call these methods in each deletion.

private void MaxHeapify(int i)
{
    int left = ChildL(i);
    int right = ChildR(i);

    int heighst = i;

    if (left <= heapSize && queue[heighst].Priority < queue[left].Priority)
        heighst = left;
    if (right <= heapSize && queue[heighst].Priority < queue[right].Priority)
        heighst = right;

    if (heighst != i)
    {
        Swap(heighst, i);
        MaxHeapify(heighst);
    }
}
private void MinHeapify(int i)
{
    int left = ChildL(i);
    int right = ChildR(i);

    int lowest = i;

    if (left <= heapSize && queue[lowest].Priority > queue[left].Priority)
        lowest = left;
    if (right <= heapSize && queue[lowest].Priority > queue[right].Priority)
        lowest = right;

    if (lowest != i)
    {
        Swap(lowest, i);
        MinHeapify(lowest);
    }
}

Two methods BuildHeapMax and BuildHeapMin we will call in every insertion to make sure Heap property is maintained.

/// <summary>
/// Maintain max heap
/// </summary>
/// <param name="i"></param>
private void BuildHeapMax(int i)
{
    while (i >= 0 && queue[(i - 1) / 2].Priority < queue[i].Priority)
    {
        Swap(i, (i - 1) / 2);
        i = (i - 1) / 2;
    }
}
/// <summary>
/// Maintain min heap
/// </summary>
/// <param name="i"></param>
private void BuildHeapMin(int i)
{
    while (i >= 0 && queue[(i - 1) / 2].Priority > queue[i].Priority)
    {
        Swap(i, (i - 1) / 2);
        i = (i - 1) / 2;
    }
}

Now let's implement Enqueue method

/// <summary>
/// Enqueue the object with priority
/// </summary>
/// <param name="priority"></param>
/// <param name="obj"></param>
public void Enqueue(int priority, T obj)
{
    Node node = new Node() { Priority = priority, Object = obj };
    queue.Add(node);
    heapSize++;
    //Maintaining heap
    if (_isMinPriorityQueue)
        BuildHeapMin(heapSize);
    else
        BuildHeapMax(heapSize);
}

Enqueue method first inserts object in list then calls BuildHeapMax or BuildHeapMin methods based on whether queue is Min-Queue or Max-Queue.

/// <summary>
/// Dequeue the object
/// </summary>
/// <returns></returns>
public T Dequeue()
{
    if (heapSize > -1)
    {
        var returnVal = queue[0].Object;
        queue[0] = queue[heapSize];
        queue.RemoveAt(heapSize);
        heapSize--;
        //Maintaining lowest or highest at root based on min or max queue
        if (_isMinPriorityQueue)
            MinHeapify(0);
        else
            MaxHeapify(0);
        return returnVal;
    }
    else
        throw new Exception("Queue is empty");
}

Dequeue method returns first object in queue and places last element at first then it calls MinHeapify or MaxHeapify methods to maintain heap.

Let's run above code

static void Main(string[] args)
{
    PriorityQueue<int> queue = new PriorityQueue<int>();
    Random rnd = new Random();
    //enqueue
    for (int i = 0; i < 10; i++)
    {
        int x = rnd.Next(3);
        queue.Enqueue(x, x);
    }
    //dequeue
    while (queue.Count > 0)
    {
        Console.WriteLine(queue.Dequeue());
    }
    Console.ReadLine();
}

Output

2

2

2

2

1

1

1

1

0

0

We can see above output is prioritized. There are two additional methods UpdatePriority and IsInQueue I would like to implement.

/// <summary>
/// Updating the priority of specific object
/// </summary>
/// <param name="obj"></param>
/// <param name="priority"></param>
public void UpdatePriority(T obj, int priority)
{
    int i = 0;
    for (; i <= heapSize; i++)
    {
        Node node = queue[i];
        if (object.ReferenceEquals(node.Object, obj))
        {
            node.Priority = priority;
            if (_isMinPriorityQueue)
            {
                BuildHeapMin(i);
                MinHeapify(i);
            }
            else
            {
                BuildHeapMax(i);
                MaxHeapify(i);
            }
        }
    }
}
/// <summary>
/// Searching an object
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public bool IsInQueue(T obj)
{
    foreach (Node node in queue)
        if (object.ReferenceEquals(node.Object, obj))
            return true;
    return false;
}

Above methods can be used to update priority of an object and finding an object in queue.

Conclusion

    This priority queue can run in O(log n) time where ordinary queues run in O(1) time. Priority queues are required in some of graph algorithms like Prim's Minimum Spanning Tree and Dijkstra Shortest Path. There are other uses of priority queues like priority operations in Operating Systems.


Like 0 People
Last modified on 14 January 2019
Nikhil Joshi

Nikhil Joshi
Ceo & Founder at Dotnetlovers
Atricles: 126
Questions: 9
Given Best Solutions: 8 *

Comments:

No Comments Yet

You are not loggedin, please login or signup to add comments:

Existing User

Login via:

New User



x