在实现一些算法的时候,会用到大大顶堆和小顶堆,下面介绍两种在C++中实现队的两种方式。
make_heap(): 生成堆,他有两个参数,也可以有三个参数,前两个参数是指向迭代器的开始元素和指向迭代器的结束元素。第三个参数是可选的,可以用伪函数less()和greater()来生成大顶堆和小顶堆,其中type为元素类型。**如果只传入前两个参数,默认是生成大顶堆,**即第三个参数的默认值的less()。
**push_heap():**是在堆的基础上进行数据的插入操作,参数与make_heap()相同,需要注意的是,只有make_heap()和push_heap()同为大顶堆或小顶堆,才能插入。
**pop_heap():是在堆的基础上,弹出堆顶元素。这里需要注意的是,pop_heap()并没有删除元素,而是将堆顶元素和数组最后一个元素进行了替换,如果要删除这个元素,还需要对数组进行pop_back()操作。可见想要删除栈顶元素,需要先将栈顶元素换到容器末尾,然后在删除容器末尾的元素。**同样需要注意的是pop_heap参数与make_heap()相同,只有make_heap()和pop_heap()同为大顶堆或小顶堆,才能弹出。
若用make_heap(), pop_heap(), push_heap(),需要添加头文件# include
。
用less ()和greater () 需要添加头文件 # include
。
这里只是描述用法,粘贴代码如下:
# include
# include
# include
# include
using namespace std;
void printVec(vector<int> nums)
{
for (int i = 0; i < nums.size(); ++i)
cout << nums[i] << " ";
cout << endl;
}
int main(void)
{
int nums_temp[] = { 8, 3, 4, 8, 9, 2, 3, 4 };
vector<int> nums(nums_temp, nums_temp + 8);
cout << "sorce nums: "; printVec(nums);
cout << "(默认)make_heap: ";
make_heap(nums.begin(), nums.end()); //默认是大顶堆,只是将最大值的元素和第一个元素互换,其他元素的顺序不变
printVec(nums);
cout << "(less)make_heap: ";
make_heap(nums.begin(), nums.end(), less<int>()); //创建大顶堆,只是将最大值的元素和第一个元素互换,其他元素的顺序不变
printVec(nums);
cout << "(greater)make_heap: ";
make_heap(nums.begin(), nums.end(), greater<int>()); //创建小顶堆,只是将最小值的元素和第一个元素互换,其他元素的顺序不变
printVec(nums);
cout << "此时,nums为小顶堆" << endl;
cout << "push_back(3)" << endl;
nums.push_back(3); //在vector的末尾添加元素
cout << "忽略第三个参数,即为默认)push_heap: ";
//下面的一行代码,相当于是往vector中添加元素后更新heap,执行到此处会出现错误,只有make_heap()和push_heap()同为大顶堆或小顶堆,才能更新。
//但是此处push_heap使用默认参数,是更新大顶堆,但是上面最后一个make_heap创建的是小顶堆,冲突。
//push_heap(nums.begin(), nums.end());
printVec(nums);
cout << "第三个参数为greater: ";
push_heap(nums.begin(), nums.end(), greater<int>()); //指定参数后不发生冲突
printVec(nums);
cout << "(做替换)默认参数pop_heap: ";
//pop_heap()并没有删除元素,而是将堆顶元素和数组最后一个元素进行了替换。和上面的push_heap一样第三个参数不指定的话,会发生冲突
//pop_heap(nums.begin(), nums.end());
printVec(nums);
cout << "(做替换)第三个参数为greater,pop_heap: ";
pop_heap(nums.begin(), nums.end(), greater<int>()); //指定参数后不发生冲突
printVec(nums);
cout << "pop_back(): ";
nums.pop_back(); //删除vector末尾的元素
printVec(nums);
}
使用的时候可以不make_heap,而直接push_heap,示例如下面的这道剑指offer上的题目数据流中的中位数。
题目描述:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
答案:
class Solution {
public:
void Insert(int num)
{
if (((min.size() + max.size()) & 1) == 0){ //偶数,如果你不知道运算符的优先级,请一定带括号
if (max.size() > 0 && num < max[0]){
max.push_back(num);
push_heap(max.begin(), max.end(), less<int>());
num = max[0];
pop_heap(max.begin(), max.end(), less<int>());
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(), min.end(), greater<int>());
}
else{
if (min.size() > 0 && num > min[0]){
min.push_back(num);
push_heap(min.begin(), min.end(), greater<int>());
num = min[0];
pop_heap(min.begin(), min.end(), greater<int>());
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(), max.end(), less<int>()); //push_back之后一定要使用push_head更新heap
}
}
double GetMedian()
{
int size = min.size() + max.size();
if (size<1)
throw "invalid!";
double median = 0;
if ((size & 1) == 1){
median = min[0];
}
else{
median = (double)((min[0] + max[0]) / 2.0);
}
return median;
}
private:
vector<int> min; //偶数
vector<int> max; //奇数
};
使用C++中优先队列也可以实现堆,使用方法如下。
首先,优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
定义: priority_queue
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆 。
一般的使用方式:
/升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
/*greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。
其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)*/
1. 使用基本类型的例子
#include
#include
#include
#include
using namespace std;
int main()
{
//对于基础类型 默认是大顶堆
priority_queue<int> a;
//等同于
//priority_queue, less> a; //这里一定要有空格,不然成了右移运算
priority_queue<int, vector<int>, greater<int> > c; //这样就是小顶堆
priority_queue<string> b;
for (int i = 0; i < 5; i++)
{
a.push(i);
c.push(i);
}
while (!a.empty())
{
cout << a.top() << ' ';
a.pop();
}
cout << endl;
while (!c.empty()) {
cout << c.top() << ' ';
c.pop();
}
cout << endl;
b.push("abc");
b.push("abcd");
b.push("cbd");
while (!b.empty())
{
cout << b.top() << ' ';
b.pop();
}
cout << endl;
return 0;
}
输出
4 3 2 1 0
0 1 2 3 4
cbd abcd abc
2.pari的比较
先比较第一个元素,第一个相等比较第二个。顺便学习pair的使用方式。
#include
#include
#include
using namespace std;
int main()
{
priority_queue<pair<int, int> > a;
pair<int, int> b(1, 2);
pair<int, int> c(1, 3);
pair<int, int> d(2, 5);
a.push(d);
a.push(c);
a.push(b);
while (!a.empty())
{
cout << a.top().first << ' ' << a.top().second << '\n';
a.pop();
}
}
3. 使用自定义的类型
#include
#include
using namespace std;
//方法1
struct tmp1 //运算符重载<
{
int x;
tmp1(int a) { x = a; }
bool operator<(const tmp1& a) const {
return x < a.x; //大顶堆
}
};
//方法2
struct tmp2 //重写仿函数
{
bool operator() (tmp1 a, tmp1 b)
{
return a.x < b.x; //大顶堆
}
};
int main()
{
tmp1 a(1);
tmp1 b(2);
tmp1 c(3);
priority_queue<tmp1> d;
d.push(b);
d.push(c);
d.push(a);
while (!d.empty()) {
cout << d.top().x << '\n';
d.pop();
}
cout << endl;
priority_queue<tmp1, vector<tmp1>, tmp2> f;
f.push(c);
f.push(b);
f.push(a);
while (!f.empty()) {
cout << f.top().x << '\n';
f.pop();
}
}
输出
3
2
1
3
2
1
参考文章:
- c++优先队列(priority_queue)用法详解
- make_heap(), pop_heap(), push_heap()用法