priority_queue优先级队列(堆)详解。C++经验+1

什么是堆

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

 大顶堆:顶上元素最大,上一层比下一层元素大。

小顶堆:顶上元素最小,上一层比下一层元素小。

什么是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的实现,就没什么问题了。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值