A priority queue is an abstract data type similar to a regular queue or stack data structure, but where each element has a priority associated with it. Elements with higher priority are dequeued before elements with lower priority. Here’s an overview of operations on a priority queue, algorithms, and their analyses.
Basic Operations
- Insert (Add an element with a priority)
- Remove (Remove and return the element with the highest priority)
- Peek (Return the element with the highest priority without removing it)
- IsEmpty (Check if the queue is empty)
Implementation
Priority queues can be implemented using various data structures, including:
- Array/List
- Linked List
- Binary Heap (most common)
- Fibonacci Heap
For this explanation, we’ll focus on the binary heap implementation, specifically a min-heap or max-heap, depending on whether we want the highest or lowest priority to come out first.
Binary Heap Structure
In a binary heap, the properties are:
- It is a complete binary tree.
- For a max-heap, each parent node is greater than or equal to its children.
- For a min-heap, each parent node is less than or equal to its children.
Operations
1. Insert Operation
void insert(int* heap, int* size, int value) {
heap[*size] = value;
(*size)++;
int i = *size – 1;
// Fix the max-heap property if it is violated
while (i != 0 && heap[(i – 1) / 2] < heap[i]) {
int temp = heap[i];
heap[i] = heap[(i – 1) / 2];
heap[(i – 1) / 2] = temp;
i = (i – 1) / 2;
}
}
Analysis:
- Time Complexity: O(log n) – In the worst case, the new element may need to be bubbled up the height of the heap.
- Space Complexity: O(1) – The space used for the array is fixed.
2. Remove Operation
int removeMax(int* heap, int* size) {
if (*size <= 0) return -1; // Heap is empty
if (*size == 1) {
(*size)–;
return heap[0];
}
int root = heap[0];
heap[0] = heap[*size – 1];
(*size)–;
// Fix the heap property
int i = 0;
while (i < *size) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < *size && heap[left] > heap[largest]) {
largest = left;
}
if (right < *size && heap[right] > heap[largest]) {
largest = right;
}
if (largest == i) break;
int temp = heap[i];
heap[i] = heap[largest];
heap[largest] = temp;
i = largest;
}
return root; // Return the removed element
}
Analysis:
- Time Complexity: O(log n) – The element may need to be bubbled down the height of the heap.
- Space Complexity: O(1).
3. Peek Operation
int peek(int* heap) {
if (heap == NULL) return -1; // Heap is empty
return heap[0]; // Return the root element
}
Analysis:
- Time Complexity: O(1) – Constant time to access the root.
- Space Complexity: O(1).
4. IsEmpty Operation
int isEmpty(int* size) {
return *size == 0; // Returns 1 if empty, otherwise 0
}
Analysis:
- Time Complexity: O(1) – Constant time check.
- Space Complexity: O(1).
Example Usage
Here’s how to use the priority queue in a simple program:
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
void insert(int* heap, int* size, int value);
int removeMax(int* heap, int* size);
int peek(int* heap);
int isEmpty(int* size);
int main() {
int heap[MAX_SIZE];
int size = 0;
insert(heap, &size, 10);
insert(heap, &size, 20);
insert(heap, &size, 30);
printf(“Max: %d\n”, peek(heap)); // Output: 30
printf(“Removed: %d\n”, removeMax(heap, &size)); // Output: 30
printf(“Max: %d\n”, peek(heap)); // Output: 20
return 0;
}
A priority queue allows for efficient management of elements based on priority. Using a binary heap provides efficient operations for insertion and removal, both in O(log n) time, making it suitable for applications like scheduling, simulation, and graph algorithms. The simple operations like peek and isEmpty allow for quick checks on the queue’s state.