跳到主要内容

优先队列

提示
  1. 优先队列基本概念:一种特殊队列,元素按优先级排列,高优先级元素先出队,具有相同优先级的元素按入队顺序出队。
  2. 实现方式:可通过数组、链表、堆等数据结构实现,其中堆(尤其是二叉堆)是实现优先队列的有效方式。
  3. 主要操作:包括插入新元素、删除元素和查看最大/最小元素。堆数据结构的堆化操作在优先队列中发挥关键作用。

优先队列是一种特殊的队列,其中每个元素都与一个优先级值相关联。元素根据其优先级服务,也就是说,优先级较高的元素先服务。

但是,如果具有相同优先级的元素出现,它们将根据在队列中的顺序服务。

分配优先级值

通常,将元素本身的值视为分配优先级的依据。例如,

具有最高值的元素被视为具有最高优先级。但是,在其他情况下,我们可以将具有最低值的元素视为最高优先级元素。

我们也可以根据需要设置优先级。

从优先队列中移除具有最高优先级的元素

优先队列与普通队列的区别

在队列中,实施“先进先出”规则,而在优先队列中,根据优先级来移除值。首先删除具有最高优先级的元素。

优先队列的实现

可以使用数组、链表、堆数据结构或二叉搜索树来实现优先队列。在这些数据结构中,堆数据结构提供了有效的优先队列实现。

因此,在本教程中,我们将使用堆数据结构来实现优先队列。下面介绍了最大堆的实现操作。如果您想了解更多信息,请访问max-heap and min-heap

下面给出了不同优先队列实现的比较分析。

操作查看插入删除
链表O(1)O(n)O(1)
二叉堆O(1)O(log n)O(log n)
二叉搜索树O(1)O(log n)O(log n)

优先队列操作

优先队列的基本操作包括插入、删除和查看元素。

在学习优先队列之前,请参考堆数据结构以更好地理解二叉堆,因为本文中使用它来实现优先队列。

1. 将元素插入优先队列

将元素插入优先队列(最大堆)的步骤如下:

  • 将新元素插入树的末尾。 将元素插入优先队列的末尾

  • 堆化树。 在插入新元素后对优先队列进行堆化

插入元素到优先队列(最大堆)的算法

如果没有节点,
创建一个新节点。
否则(已经存在一个节点)
将新节点插入到末尾(从左到右的最后一个节点)。

对数组进行堆化

对于最小堆,上述算法进行了修改,以使parentNode始终小于newNode

2. 从优先队列中删除元素

从优先队列(最大堆)中删除元素的步骤如下:

  • 选择要删除的元素。 选择要从优先队列中删除的元素

  • 与最后一个元素交换。 将要删除的元素与最后一个叶节点元素交换

  • 删除最后一个元素。 ![删除最后一个叶节点

元素](img/delete-3.png)

  • 对树进行堆化。 对优先队列进行堆化

从优先队列(最大堆)中删除元素的算法

如果要删除的节点是叶节点
删除节点
否则,将要删除的节点与最后一个叶节点交换
删除要删除的节点

对数组进行堆化

对于最小堆,上述算法进行了修改,以使两个childNodes均小于currentNode

3. 查看优先队列(查找最大/最小值)

查看操作从最大堆返回最大元素,从最小堆返回最小元素,而不删除节点。

对于最大堆和最小堆都是如下操作:

返回根节点

4. 从优先队列中提取最大/最小值

Extract-Max从最大堆中删除具有最大值的节点,而Extract-Min从最小堆中删除具有最小值的节点。

Python、Java、C 和 C++ 中的优先队列实现

Python Java C C++

# Python中的优先队列实现

# 堆化树的函数
def heapify(arr, n, i):
# 找到根、左子节点和右子节点中最大的一个
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

# 如果根不是最大的,则交换并继续堆化
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)


# 插入元素到树的函数
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)


# 从树中删除元素的函数
def deleteNode(array, num):
size = len(array)
i = 0
for i in range(0, size):
if num == array[i]:
break

array[i], array[size - 1] = array[size - 1], array[i]

array.remove(size - 1)

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 ("最大堆数组: " + str(arr))

deleteNode(arr, 4)
print("删除一个元素后: " + str(arr))
// Java中的优先队列实现

import java.util.ArrayList;

class Heap {
// 堆化树的函数
void heapify(ArrayList<Integer> hT, int i) {
int size = hT.size();
// 找到根、左子节点和右子节点中最大的一个
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;

// 如果根不是最大的,则交换并继续堆化
if (largest != i) {
int temp = hT.get(largest);
hT.set(largest, hT.get(i));
hT.set(i, temp);

heapify(hT, largest);
}
}

// 插入元素到树的函数
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);
}
}
}

// 从树中删除元素的函数
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);
}
}

// 打印树
void printArray(ArrayList<Integer> array, int size) {
for (Integer i : array) {
System.out.print(i + " ");
}
System.out.println();
}

// 主程序
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("最大堆数组: ");
h.printArray(array, size);

h.deleteNode(array, 4);
System.out.println("删除一个元素后: ");
h.printArray(array, size);
}
}

希望这可以帮助你了解优先队列的工作原理

// 在C中实现的优先队列

#include <stdio.h>
int size = 0;
void swap(int *a, int *b) {
int temp = *b;
*b = *a;
*a = temp;
}

// 堆化树的函数
void heapify(int array[], int size, int i) {
if (size == 1) {
printf("堆中只有一个元素");
} else {
// 找到根、左子节点和右子节点中最大的一个
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;

// 如果根不是最大的,则交换并继续堆化
if (largest != i) {
swap(&array[i], &array[largest]);
heapify(array, size, largest);
}
}
}

// 插入元素到树的函数
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);
}
}
}

// 从树中删除元素的函数
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);
}
}

// 打印数组
void printArray(int array[], int size) {
for (int i = 0; i < size; ++i)
printf("%d ", array[i]);
printf("\n");
}

// 主程序
int main() {
int array[10];

insert(array, 3);
insert(array, 4);
insert(array, 9);
insert(array, 5);
insert(array, 2);

printf("最大堆数组: ");
printArray(array, size);

deleteRoot(array, 4);

printf("删除一个元素后: ");

printArray(array, size);
}
// 在C++中实现的优先队列

#include <iostream>
#include <vector>
using namespace std;

// 交换两个元素位置的函数
void swap(int *a, int *b) {
int temp = *b;
*b = *a;
*a = temp;
}

// 堆化树的函数
void heapify(vector<int> &hT, int i) {
int size = hT.size();

// 找到根、左子节点和右子节点中最大的一个
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;

// 如果根不是最大的,则交换并继续堆化
if (largest != i) {
swap(&hT[i], &hT[largest]);
heapify(hT, largest);
}
}

// 插入元素到树的函数
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);
}
}
}

// 从树中删除元素的函数
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);
}
}

// 打印树
void printArray(vector<int> &hT) {
for (int i = 0; i < hT.size(); ++i)
cout << hT[i] << " ";
cout << "\n";
}

// 主程序
int main() {
vector<int> heapTree;

insert(heapTree, 3);
insert(heapTree, 4);
insert(heapTree, 9);
insert(heapTree, 5);
insert(heapTree, 2);

cout << "最大堆数组: ";
printArray(heapTree);

deleteNode(heapTree, 4);

cout << "删除一个元素后: ";

printArray(heapTree);
}

优先队列应用

一些优先队列的应用包括:

  • Dijkstra算法
  • 用于实现栈
  • 在操作系统中用于负载平衡和中断处理
  • 在Huffman编码中用于数据压缩