1.优先级队列的介绍
优先级队列是一种特殊的队列数据结构,它是队列,但又不完全是,因为它要将装载的数据进行优先级排序,找到一个最大或者最小优先级的元素,下一次出队列的元素就是这个元素,所以说它不完全是一个队列,优先级队列广泛应用于任务调度、资源分配、事件处理、Dijkstra算法、A*搜索算法等领域。
2.优先级队列的设计
个人感觉,设计一个优先级队列就是一个堆建立和调整的过程,因为要装载元素,所以我们采用vector来作为成员变量,后续就是对与这个vector变量的管理就行了,因为我们只是简单设计一个优先级队列,所以并没有设计在优先级队列中传入函数进行比较的过程,只是简单的比较大小的过程,首先我们要设计向上调整函数和向下调整函数,因为这个的构造函数和析构函数非常简单,这里我就不过多赘述了。
向上调整函数
向上调整函数就是子节点与父节点做比较,这里我们假设建小堆,如果子节点小于父节点,那么这个堆就是有问题的,所以要进行交换,将子节点与父节点进行交换,但是我们又要考虑交换以后得子节点也就是现在的父节点是否和他现在的父节点又是不对的关系的,所以我们要进行循环,只有当节点为0,也就是根节点或者,子节点小于父节点时,进行循环退出
void siftUp(size_t index)
{
while (index > 0)
{
int father = index / 2;
if (list[father] > list[index])
{
swap(list[father], list[index]);
index = father;
}
else
{
break;
}
}
}
向下调整函数
向下调整就是判断你是否比你的两个孩子都要小,逻辑和向上调整函数差不多,只是要和两个孩子都比一次,或者找最小的那个孩子比,主要是要知道孩子是index/2-1和index/2-2就行
void siftDown(size_t index)
{
while (index <= list.size())
{
leftson = index * 2 + 1;
rightson = index * 2 + 2;
if (list[index] > list[leftson])
{
swap(list[index], list[leftson])
index = leftson;
}
else if (list[index] > list[rightson])
{
swap(list[index], list[rightson]);
index = rightson;
}
else
{
break;
}
}
}
建堆函数
在构造函数的第一步肯定是拷贝构造对应的vector,然后得到的这个vector大概率不是一个堆,因此要对它进行调整,使得它可以成为一个符合规则的堆,一般建堆有两种策略,一种是自下而上的sift down方法,另一种是自上而下的sift up方法。这里我们使用sit down 向下调整的方法,找到最后一个非叶子节点,也就是list.size()/2,然后对每个非叶子节点进行向下调整
explicit Heap(const std::vector<T>& array)
:list(array)
{
buildHeap();
}
void buildHeap()
{
for (int i = list.size() / 2 ; i >=0 ; i--)
{
siftDown(i);
}
}
插入函数
当我们插入一个元素时,我们首先是尾插入vector列表,然后对这个元素进行向上调整,就使得这个堆变得规则
void insert(const T& value)
{
list.insert(value);
siftUp(list.size() - 1);
}
获取顶端元素函数
我们建立这个堆或者说维护这个优先级队列肯定是为了得到一个有价值的元素,所有这个堆里面最有价值的元素当然时堆顶元素,但是当我们得到堆顶元素时,这个堆也会被破坏,所以,我们一般会将堆顶元素和最后一个元素进行交换,然后再对堆顶元素进行向下调整,就让堆保持合适的规则
void removeTop()
{
if (list.empty()) {
throw std::out_of_range("Heap is empty");
}
swap(list[0], list[list.size() - 1]);
siftDown(0);
list.erase(list.end() - 1);
}