定义
优先队列(PriorityQueue),根据key值的大小将元素进行排序、先被pop的通常是优先级最高的。此处介绍基于堆实现的优先队列,binary heap是一种完全二叉树,以大堆为例,每棵树的根节点的key值一定大于其子孙节点的key值,完全二叉树除了最底层的叶子节点外,其他位置都是填满的。这样我们可以利用数组来存储所有节点。
若将数组从下标1开始存储元素、那么下标为 i 的节点的左孩子节点的下标为 2 * i、而右孩子节点下标为 2 * i + 1;这样可以很容易定位一个节点的孩子节点或父节点的在数组中位置。
插入数据
- 添加元素,也就是需要将元素添加到最底层叶子节点从左向右的空位置上。就是vector的最后一个位置。
- 通过下标计算出新节点的父亲节点、与父节点key值比较、若比父节点大、则与父节点进行位置交换、不断上溯直到比父节点小或者父节点为空(也就是本身成为了根节点)。
- 在与父节点交换时、只需要交换他们的值就好。
- 插入一个111,首先把111 插入到底部
- 调整堆,把111 和 111的顶部parentNode进行比较,发现111大于33,所有需要改变exchange 111 和 33的位置
- 接着111 和 parentNode比,发现还是大于ParentNode,所以继续改变位置。
- 继续比较
- 到达top了,没有元素比111大了。此时堆变得有序了。
删除数据
- 因为是队列、每次pop是删除都是删除根节点。
- 由于要保证heap的complete binary tree结构、每次删除的节点实际上都是最后一块内存空间(vector中最后一个位置)。
- 删除根节点时需要比较左右节点的key值、与删除节点进行交换,然后以删除节点位置开始向下查找(重复)。
- 首先我们先把111和 最底部的元素交换位置,并且取出111。
- 把top部的元素和2个子元素比较大小,如果比子元素小的话,需要交换他们的位置,把33 和 Math.max(100, 50)交换位置。
- 33 比 90 小,交换他们的位置
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
class PriorityQueue {
private:
// 在heap内部维护一个数组vector用来保存元素、方便建立堆和节点的添加删除
vector<T> v;
int size;
const static int initCap = 100;
private:
int parent(int pos) {
return pos/2;
}
int left(int pos) {
return pos*2;
}
int right(int pos) {
return pos*2 + 1;
}
public:
PriorityQueue():
size(0) {
v = vector<T>(initCap);
}
bool enQueue(const T& t) {
size++;
v[size] = t;
int i = size;
//插入到尾部,一直上提到使当前堆符合最大堆性质为止
while(i > 1 && v[i] > v[parent(i)]) {
T tmp = v[i];
v[i] = v[parent(i)];
v[parent(i)] = tmp;
i = parent(i);
}
return true;
}
bool deQueue(T& max) {
//取出最大值,并把尾部元素和最大值交换,然后维护当前堆到符合堆性质
if(size < 1) {
return false;
}
max = v[1];
v[1] = v[size];
v[size] = max;
size--;
maxHeapIfy(1);
return true;
}
void maxHeapIfy(int pos) {
int largestPos = pos;
if(left(pos) <= size && v[pos] < v[left(pos)]) {
largestPos = left(pos);
}
if(right(pos) <= size && v[largestPos] < v[right(pos)]) {
largestPos = right(pos);
}
if(largestPos != pos) {
T tmp = v[pos];
v[pos] = v[largestPos];
v[largestPos] = tmp;
maxHeapIfy(largestPos);
}
}
int getSize() {
return size;
}
bool top(T& t) {
if(size < 1) {
return false;
}
t = v[1];
return true;
}
};
int main() {
PriorityQueue<int> pQ;
pQ.enQueue(8);
pQ.enQueue(1);
pQ.enQueue(3);
pQ.enQueue(6);
pQ.enQueue(5);
pQ.enQueue(3);
pQ.enQueue(10);
pQ.enQueue(9);
pQ.enQueue(7);
int a = -1;
while(pQ.deQueue(a)) {
cout<<a<<" ";
}
cout<<endl;
return 0;
}