优先队结构的不同物理结构与常用操作算法
优先队列是一种特殊的队列,队列中的元素具有优先级,每次弹出操作会弹出优先级最高的元素。
优先队列常用的物理结构有:
1. 数组:简单但不高效,插入和删除操作需要移动大量元素,时间复杂度高。
2. 二叉堆:是一种完全二叉树,通常用数组表示。插入和删除操作时间复杂度为O(logn) 。
3. 二叉搜索树:节点值大于左子树所有节点,小于右子树所有节点。插入和删除操作时间复杂度取决于树的高度,平衡二叉搜索树时间复杂度为O(logn) 。
常用操作设计:
1. 插入元素:将新元素插入到合适的位置,维持堆的性质。
2. 删除最大/最小元素:删除根节点元素,并调整堆的结构。
3. 获取最大/最小元素:直接返回堆顶元素。
4. 堆大小:返回堆中的元素个数。
5. 堆空:判断堆是否为空。
使用数组结构实现最小堆的代码:
#include <iostream>using namespace std;template <typename T>
class PriorityQueue
{
private:T *arr;int capacity;int size;public:PriorityQueue(int cap) //创造队列{capacity = cap;size = 0;arr = new T[cap];}int getSize(){return size;}bool isEmpty(){return size == 0;}void insert(T elem){if (size == capacity){return;}size++;arr[size - 1] = elem;int i = size - 1;while (i > 0 && arr[i] < arr[(i - 1) / 2]){swap(arr[i], arr[(i - 1) / 2]);i = (i - 1) / 2;}}T getMin() // 返回最小值{return arr[0];}T extractMin() // 提取最小值{if (isEmpty()){return -1;}T root = arr[0];arr[0] = arr[size - 1];size--;int i = 0;while (2 * i + 1 < size){int leftChild = 2 * i + 1;int rightChild = 2 * i + 2;if (rightChild < size && arr[leftChild] > arr[rightChild]){leftChild = rightChild;}if (arr[i] <= arr[leftChild]){break;}swap(arr[i], arr[leftChild]);i = leftChild;}return root;}bool find(T elem) //查找{for (int i = 0; i < size; i++){if (arr[i] == elem){return true;}}return false;}void deleteElem(T elem) //删除队列元素{if (isEmpty() || !find(elem)){return;}int i;for (i = 0; i < size; i++){if (arr[i] == elem){break;}}arr[i] = arr[size - 1];size--;while (i < size && arr[i] < arr[(i - 1) / 2]){swap(arr[i], arr[(i - 1) / 2]);i = (i - 1) / 2;}while (2 * i + 1 < size){int leftChild = 2 * i + 1;int rightChild = 2 * i + 2;if (rightChild < size && arr[leftChild] > arr[rightChild]){leftChild = rightChild;}if (arr[i] <= arr[leftChild]){break;}swap(arr[i], arr[leftChild]);i = leftChild;}}
};int main()
{PriorityQueue<int> pq(5);pq.insert(3);pq.insert(1);pq.insert(4);cout << pq.getMin() << endl;cout << pq.extractMin() << endl;cout << pq.getSize() << endl;pq.deleteElem(4);cout << pq.find(4) << endl;return 0;
}
运行结果截图:
最小堆和最大堆只有比较函数不同。最小堆使用 < 比较,最大堆使用 > 比较。所以要将最小堆修改为最大堆,只需要修改比较函数即可。这里对应的修改代码为:
T getMax() // 返回最大值
{ return arr[0];
}T extractMax() // 提取最大值
{ if (isEmpty()) { return -1; } T root = arr[0]; arr[0] = arr[size - 1]; size--; int i = 0; while (2 * i + 1 < size) { int leftChild = 2 * i + 1; int rightChild = 2 * i + 2; if (rightChild < size && arr[leftChild] < arr[rightChild]) { leftChild = rightChild; }if (arr[i] >= arr[leftChild]) { break; } swap(arr[i], arr[leftChild]); i = leftChild; } return root;
}
可以看到,这里把 < 改为 > ,将 getMin() 改为 getMax(),将 extractMin() 改为 extractMax()。这样,堆中最大的值会在堆顶,所以这就是一个最大堆了。
总结
优先队列是一种抽象数据结构,定义了插入、删除和获取最大/最小元素等操作。堆是一种具体的数据结构,可以用来实现优先队列。堆是一个近似完全二叉树,并且满足堆序性质:父节点的值总是大于(或小于)子节点的值。
二叉堆是一种特殊的堆,完全二叉树,通常用数组表示。二叉堆很适合实现优先队列,时间复杂度为O(logn) 。优先队列强调数据元素的优先级与排序,堆提供了一种数据结构来高效实现这一功能。
参考文献
[1]张铭,王腾蛟,赵海燕编著,《数据结构与算法》,高等教育出版社,2008.6
[2] (美) 乔兹德克(Drozdek, A.) 著 徐丹,吴伟敏 译 C++数据结构与算法(第4版)(国外计算机科学经典教材)清华大学出版社,2014-10-01
[3] (美)萨尼 著,王立柱,刘志红 译,数据结构、算法与应用 C++语言描述(原书第2版)机械工业出版社,2015-04-01