什么是堆
首先我们先了解什么是堆?堆分为大根堆和小根堆。但其实大根堆会让人误以为是不是大的元素在下面呢?为了防止错误想法,大根堆也可以叫大顶堆。

大顶堆:顶上元素最大,上一层比下一层元素大。
小顶堆:顶上元素最小,上一层比下一层元素小。
什么是priority_queue优先级队列
priority_queue就是用vector 作为 模版,然后里面的数据填充的方法用的是堆算法。所以priority_queue就是堆。只不过是提供了对应接口的堆。不用手动去敲。
怎么定义priority_queue对象
方法一:默认定义(只指定是什么元素)
priority_queue<int> q;默认定义:造出来的直接就是一个大顶堆。
方法二:定义大顶堆(指定元素以及用什么当模版以及比较函数)
priority_queue<int, vector<int>, less<int>> q1;上面不是说priority_queue用vector做模版吗?为什么这里我们还要写一遍。因为对于template模版 而言缺省值,只能从右给到左,如果要修改最后一个模版less<int>,那对应的前面的模版都得手动填写。
为什么大顶堆用less,因为less只是比较函数,比大小的和大顶堆的定义不冲突。可以想的是用less的话是小的元素往下跑。
方法三:定义小顶堆(指定元素以及用什么当模版以及比较函数)
priority_queue<int, vector<int>, greater<int>> q2;和大顶堆一样。
priority_queue的成员函数(方法接口)
| 成员函数 | 功能 | 
|---|---|
| push | 插入元素到队尾(并排序) | 
| pop | 弹出队头元素(堆顶元素) | 
| top | 访问队头元素(堆顶元素) | 
| size | 获取队列中有效元素个数 | 
| empty | 判断队列是否为空 | 
| swap | 交换两个队列的内容 | 
用接口实现打印有序数组。
int main()
{
	priority_queue<int> q;
	q.push(3);
	q.push(6);
	q.push(0);
	q.push(2);
	q.push(1);
	while (!q.empty())
	{
		cout << q.top() << " ";
		q.pop();
	}
	cout << endl; // 6 3 2 1 0
	return 0;
}很简单,插入无序的元素,因为是大顶堆,所以最大的元素都在上面,每次弹出元素后,会把第二大的元素放到堆顶。以此循环直到没有元素。此时就是有序的。
堆调整算法
上文说了priority_queue就是堆,那么堆算法里有两个核心:向上调整算法和向下调整算法
注意:虽然说是数组实现的,但是用二叉树的形式表示会更加明了。所以下面用二叉树的样子演示
向上调整算法:
顾名思义:就是把数据向上调整。对应的接口有:push。push时是将元素先插入堆的最后,在调用向上调整算法,找到其位置。

图一中每个节点右边的数字代表了节点在vector数组中的位置。
算法解析:插入元素后,让元素与其父节点比较,符合就替代父节点。以此往复直到不符合或者到达0位置。
代码如下:
//堆的向上调整(大堆)
void AdjustUp(vector<int>& v, int child)
{
	int parent = (child - 1) / 2; //通过child计算parent的下标
	while (child > 0)//调整到根结点的位置截止
	{
		if (v[parent] < v[child])//孩子结点的值大于父结点的值
		{
			//将父结点与孩子结点交换
			swap(v[child], v[parent]);
			//继续向上进行调整
			child = parent;
			parent = (child - 1) / 2;
		}
		else//已成堆
		{
			break;
		}
	}
}向下调整算法:
顾名思义:就是将元素向下调整,那有什么用呢?在pop元素时。有两种思路:
1.删除最上面的元素后,对每个节点再做一次向上调整算法。这个时间复杂度挺高的。
2.将最上面的元素和最后面的元素交换,然后pop最后一个元素,将目前最上面的元素做一次向下调整。对比1快了许多。所以就有了向下调整算法。

和向上调整一样,都是比较元素。不同的在于这里是向下走,所以说比较时符号是相反的。当然也可以是两个操作数交换位置,就不用把符号改变了。可以实现代码的复用
代码如下:
//堆的向下调整(大堆)
void AdjustDown(vector<int>& v, int n, int parent)
{
    //child记录左右孩子中值较大的孩子的下标
    int child = 2 * parent + 1;//先默认其左孩子的值较大
    while (child < n)
    {
        if (child + 1 < n && v[child] < v[child + 1])//右孩子存在并且右孩子比左孩子还大
        {
            child++;//较大的孩子改为右孩子
        }
        if (v[parent] < v[child])//左右孩子中较大孩子的值比父结点还大
        {
            //将父结点与较小的子结点交换
            swap(v[child], v[parent]);
            //继续向下进行调整
            parent = child;
            child = 2 * parent + 1;
        }
        else//已成堆
        {
            break;
        }
    }
}有了这两个调整算法以后,对于priority_queue的实现,就没什么问题了。


















