优先队列是一种特殊类型的队列,在这种队列中,每个元素都与一个优先级值相关联。而且,元素是根据它们的优先级来处理的。也就是说,优先级较高的元素会被优先处理。
如果出现了具有相同优先级的元素,那么它们将按照在队列中的顺序来进行处理。
分配优先级值
一般来说,在分配优先级时会考虑元素本身的值。例如,
通常,数值最大的元素会被视为优先级最高的元素。不过,在其他情况下,我们也可以将数值最小的元素假定为优先级最高的元素。
我们也可以根据自己的需求来设置优先级。
普通队列和优先队列的区别
在普通队列中,执行的是先进先出规则,然而,在优先队列中,元素是根据优先级来移除的。优先级最高的元素会首先被移除。
优先队列的实现
优先队列可以使用数组、链表、堆数据结构或二叉搜索树来实现。在这些数据结构中,堆数据结构为优先队列提供了一种高效的实现方式。
因此,在本教程中我们将使用堆数据结构来实现优先队列。以下操作中实现的是大顶堆。如果你想了解更多相关内容,请访问大顶堆和小顶堆的相关内容。
下面给出了对优先队列不同实现方式的对比分析。
操作 | 查看元素 | 插入 | 删除 |
---|---|---|---|
链表 | O(1) | O(n) | O(1) |
二叉堆 | O(1) | O(log n) | O(log n) |
二叉搜索树 | O(1) | O(log n) | O(log n) |
优先队列的操作
基本操作有插入、移除和访问元素
开始学习优先队列之前,可以看一下堆数据结构,可以对本文中实现优先队列的二叉堆有一个更好的理解
1.向优先队列中插入一个元素
向一个优先队列(大顶堆)中插入一个元素遵循以下步骤。
- 在树的底部插入一个新元素
- 堆化这棵树
向优先队列(大顶堆)中插入元素的算法
如果树中没有节点:
创建一个新节点。
否则(树中已经存在节点):
将新节点插入到树的末尾(从左到右的最后一个节点位置)。
对数组进行堆化操作
对于小顶堆,需要对上述算法进行修改,确保父节点的值始终小于新节点的值。
2.从优先队列中删除一个元素
从一个优先队列(大顶堆)中删除一个元素遵循一下操作:
- 选中要被删除的元素
- 选中的元素和最后一个元素交换
- 删除最后一个元素
- 堆化这棵树
优先队列(最大堆)中删除元素的算法
如果要删除的节点是叶节点:
移除该节点。
否则,将要删除的节点与最后一个叶节点交换:
移除要删除的节点。
对数组进行堆化操作
对于最小堆,需要对上述算法进行修改,使得两个子节点的值都小于当前节点的值
3.访问优先队列的值(找最大/最小)
访问操作会返回大顶堆中的最大元素,或者小顶堆中的最小元素,且不会删除该节点。
对大顶堆和小顶堆来说都是返回根节点(rootNode)
4.从优先队列中提取最大 / 最小值
“Extract-Max(提取最大值)” 操作是从大顶堆中移除具有最大值的节点后,返回该节点;而 “Extract-Min(提取最小值)” 操作则是从小顶堆中移除具有最小值的节点后,返回该节点。
优先队列在Python、Java、C和C++中的实现
# Priority Queue implementation in Python
# Function to heapify the tree
def heapify(arr, n, i):
# Find the largest among root, left child, and right child
largest = i
l = 2 * i + 1
r = 2 * i + 2
if l < n and arr[i] < arr[l]:
largest = l
if r < n and arr[largest] < arr[r]:
largest = r
# Swap and continue heapifying if root is not the largest
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
# Function to insert an element into the tree
def insert(array, newNum):
size = len(array)
if size == 0:
array.append(newNum)
else:
array.append(newNum)
for i in range((size // 2) - 1, -1, -1):
heapify(array, size, i)
# Function to delete an element from the tree
def deleteNode(array, num):
size = len(array)
i = 0
for i in range(0, size):
if num == array[i]:
break
# Swap the element to delete with the last element
array[i], array[size - 1] = array[size - 1], array[i]
# Remove the last element (the one we want to delete)
array.pop()
# Rebuild the heap
for i in range((len(array) // 2) - 1, -1, -1):
heapify(array, len(array), i)
arr = []
insert(arr, 3)
insert(arr, 4)
insert(arr, 9)
insert(arr, 5)
insert(arr, 2)
print("Max-Heap array: " + str(arr))
deleteNode(arr, 4)
print("After deleting an element: " + str(arr))
// Priority Queue implementation in Java
import java.util.ArrayList;
class Heap {
// Function to heapify the tree
void heapify(ArrayList<Integer> hT, int i) {
int size = hT.size();
// Find the largest among root, left child and right child
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < size && hT.get(l) > hT.get(largest))
largest = l;
if (r < size && hT.get(r) > hT.get(largest))
largest = r;
// Swap and continue heapifying if root is not largest
if (largest != i) {
int temp = hT.get(largest);
hT.set(largest, hT.get(i));
hT.set(i, temp);
heapify(hT, largest);
}
}
// Function to insert an element into the tree
void insert(ArrayList<Integer> hT, int newNum) {
int size = hT.size();
if (size == 0) {
hT.add(newNum);
} else {
hT.add(newNum);
for (int i = size / 2 - 1; i >= 0; i--) {
heapify(hT, i);
}
}
}
// Function to delete an element from the tree
void deleteNode(ArrayList<Integer> hT, int num) {
int size = hT.size();
int i;
for (i = 0; i < size; i++) {
if (num == hT.get(i))
break;
}
int temp = hT.get(i);
hT.set(i, hT.get(size - 1));
hT.set(size - 1, temp);
hT.remove(size - 1);
for (int j = size / 2 - 1; j >= 0; j--) {
heapify(hT, j);
}
}
// Print the tree
void printArray(ArrayList<Integer> array, int size) {
for (Integer i : array) {
System.out.print(i + " ");
}
System.out.println();
}
// Driver code
public static void main(String args[]) {
ArrayList<Integer> array = new ArrayList<Integer>();
int size = array.size();
Heap h = new Heap();
h.insert(array, 3);
h.insert(array, 4);
h.insert(array, 9);
h.insert(array, 5);
h.insert(array, 2);
System.out.println("Max-Heap array: ");
h.printArray(array, size);
h.deleteNode(array, 4);
System.out.println("After deleting an element: ");
h.printArray(array, size);
}
}
// Priority Queue implementation in C
#include <stdio.h>
int size = 0;
void swap(int *a, int *b) {
int temp = *b;
*b = *a;
*a = temp;
}
// Function to heapify the tree
void heapify(int array[], int size, int i) {
if (size == 1) {
printf("Single element in the heap");
} else {
// Find the largest among root, left child and right child
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < size && array[l] > array[largest])
largest = l;
if (r < size && array[r] > array[largest])
largest = r;
// Swap and continue heapifying if root is not largest
if (largest != i) {
swap(&array[i], &array[largest]);
heapify(array, size, largest);
}
}
}
// Function to insert an element into the tree
void insert(int array[], int newNum) {
if (size == 0) {
array[0] = newNum;
size += 1;
} else {
array[size] = newNum;
size += 1;
for (int i = size / 2 - 1; i >= 0; i--) {
heapify(array, size, i);
}
}
}
// Function to delete an element from the tree
void deleteRoot(int array[], int num) {
int i;
for (i = 0; i < size; i++) {
if (num == array[i])
break;
}
swap(&array[i], &array[size - 1]);
size -= 1;
for (int i = size / 2 - 1; i >= 0; i--) {
heapify(array, size, i);
}
}
// Print the array
void printArray(int array[], int size) {
for (int i = 0; i < size; ++i)
printf("%d ", array[i]);
printf("\n");
}
// Driver code
int main() {
int array[10];
insert(array, 3);
insert(array, 4);
insert(array, 9);
insert(array, 5);
insert(array, 2);
printf("Max-Heap array: ");
printArray(array, size);
deleteRoot(array, 4);
printf("After deleting an element: ");
printArray(array, size);
}
// Priority Queue implementation in C++
#include <iostream>
#include <vector>
using namespace std;
// Function to swap position of two elements
void swap(int *a, int *b) {
int temp = *b;
*b = *a;
*a = temp;
}
// Function to heapify the tree
void heapify(vector<int> &hT, int i) {
int size = hT.size();
// Find the largest among root, left child and right child
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < size && hT[l] > hT[largest])
largest = l;
if (r < size && hT[r] > hT[largest])
largest = r;
// Swap and continue heapifying if root is not largest
if (largest != i) {
swap(&hT[i], &hT[largest]);
heapify(hT, largest);
}
}
// Function to insert an element into the tree
void insert(vector<int> &hT, int newNum) {
int size = hT.size();
if (size == 0) {
hT.push_back(newNum);
} else {
hT.push_back(newNum);
for (int i = size / 2 - 1; i >= 0; i--) {
heapify(hT, i);
}
}
}
// Function to delete an element from the tree
void deleteNode(vector<int> &hT, int num) {
int size = hT.size();
int i;
for (i = 0; i < size; i++) {
if (num == hT[i])
break;
}
swap(&hT[i], &hT[size - 1]);
hT.pop_back();
for (int i = size / 2 - 1; i >= 0; i--) {
heapify(hT, i);
}
}
// Print the tree
void printArray(vector<int> &hT) {
for (int i = 0; i < hT.size(); ++i)
cout << hT[i] << " ";
cout << "\n";
}
// Driver code
int main() {
vector<int> heapTree;
insert(heapTree, 3);
insert(heapTree, 4);
insert(heapTree, 9);
insert(heapTree, 5);
insert(heapTree, 2);
cout << "Max-Heap array: ";
printArray(heapTree);
deleteNode(heapTree, 4);
cout << "After deleting an element: ";
printArray(heapTree);
}
优先队列的应用
- 迪杰斯特拉算法
- 用于实现栈
- 用于操作系统中的负载均衡和中断处理
- 用于霍夫曼编码中的数据压缩