C++ 优先级队列(运用绑定器实现大小根堆)

本文介绍了如何在C++中利用函数绑定器实现自定义大小根堆的优先级队列。通过详细讲解入堆和出堆操作,以及提供完整的代码实现,展示了如何维护大根堆的性质并进行元素的添加与删除。示例代码中展示了如何使用`std::function`和`std::greater`来定制比较函数,实现一个最小堆。

C++ 优先级队列(运用绑定器实现大小根堆)

1. 概要

在C++的优先级队列priority_queue中,其实是使用大小根堆实现的(默认是大根堆)。
在这里,我们使用了函数绑定器functional绑定了一根函数对象来自定义的实现大小根堆。

另外,对于实现一个大小根堆,有两个重要的操作需要考虑:

  • 入堆操作
  • 出堆操作

1. 1 入堆操作

对于入堆,一开始其实把入堆的元素放到数组的最后面。放在最后的这个元素在二叉树里面相当于就在叶子结点中。

但是,对于大根堆来说,每个节点的父节点是需要大于它的孩子节点的。刚插入的、放在叶子节点中的这个节点可能比它的夫节点还要大,因此需要进行上浮操作。

1. 2 出堆操作

对于出堆,首先需要记录数组的第一个元素(相当于堆顶元素)的值,然后用最后一个值覆盖第一个元素。再把最后一个元素给pop掉。

这时新的堆顶元素不一定能符合父节点大于两个孩子节点的条件,因此需要进行下沉操作。

2. 代码实现

#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <assert.h>
#include <time.h>
using namespace std;

class priorityQueue {
public:
	using Cmp = function<bool(int, int)>;
	priorityQueue(Cmp cmp = greater<int>()) : cmp_(cmp) {}

	void push(int val) {
		if (this->empty()) {
			src_.push_back(val);
		} else {
			src_.push_back(val);
			siftUp();
		}
	}

	void pop() {
		assert(!this->empty());
		
		if (src_.size() == 1) {
			src_.pop_back();
		} else {
			swap(src_[0], src_[this->size() - 1]);
			src_.pop_back();
			siftDown();
		}
	}

	int top() {
		assert(!this->empty());
		return src_.front();
	}

	bool empty() {
		return src_.size() == 0;
	}

	int size() {
		return src_.size();
	}

private:
	// 上浮操作
	void siftUp() {
		int i = src_.size() - 1;
		while (i > 0) {
			int fatherIndex = (i - 1) / 2;
			// 当前结点大于父节点
			if (cmp_(src_[i], src_[fatherIndex])) {
				swap(src_[i], src_[fatherIndex]);
				i = fatherIndex;
			} else {
				break;
			}
		}
	}
	// 下沉操作
	void siftDown() {
		int i = 0;
		while (i < src_.size() / 2) {
			int lson = i * 2 + 1;
			int rson = i * 2 + 2;

			int largestIndex = i;
			if (lson < src_.size() && cmp_(src_[lson], src_[largestIndex])) {
				largestIndex = lson;
			}
			if (rson < src_.size() && cmp_(src_[rson], src_[largestIndex])) {
				largestIndex = rson;
			}

			if (i != largestIndex) {
				swap(src_[i], src_[largestIndex]);
				i = largestIndex;
			} else {
				break;
			}
		}
	}
	
private:
	vector<int> src_;
	Cmp cmp_;
};


int main() {
	priorityQueue maxHeap;
	srand(time(nullptr));
	int size = 20;
	for (int i = 0; i < size; ++i) {
		int val = rand() % 100;
		maxHeap.push(val);
	}

	while (!maxHeap.empty()) {
		int val = maxHeap.top();
		cout << val << " ";
		maxHeap.pop();
	}

	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值