参考:
https://blog.csdn.net/liu_sheng_1991/article/details/52298887
https://blog.csdn.net/li744831579/article/details/39023785
https://www.cnblogs.com/easonliu/p/4441916.html
http://cuijiahua.com/blog/2018/02/basis_63.html
STL中与堆相关的4个函数——建立堆make_heap(),在堆中添加数据push_heap(),在堆中删除数据pop_heap()和堆排序sort_heap()
头文件
#include
template <class RandomAccessIterator>
void make_heap (RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare>
void make_heap (RandomAccessIterator first, RandomAccessIterator last,
Compare comp );
可以看到,make_heap()可以有两个参数,也可以有三个参数。
第三个参数是可选的,可以用仿函数less()和greater()来生成大顶堆和小顶堆
默认是建立大顶堆。
先向容器加入数据,再调用push_heap()。
template <class RandomAccessIterator>
void push_heap (RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare>
void push_heap (RandomAccessIterator first, RandomAccessIterator last,
Compare comp);
push_heap()是在堆的基础上进行数据的插入操作,参数与make_heap()相同。
需要注意的是,只有make_heap()
和push_heap()
同为大顶堆或小顶堆,才能插入。
要先调用pop_heap()再在容器中删除数据。
template <class RandomAccessIterator>
void pop_heap (RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare>
void pop_heap (RandomAccessIterator first, RandomAccessIterator last,
Compare comp);
pop_heap()是在堆的基础上,弹出堆顶元素。
这里需要注意的是,pop_heap()并没有删除元素,而是将堆顶元素和数组最后一个元素进行了替换,如果要删除这个元素,还需要对数组进行pop_back()操作。
#include
#include
#include
#include
#include
using namespace std;
void printVec(vector<int> nums)
{
for(int i=0;icout<" ";
}
cout<int main(int argc, const char * argv[]) {
int nums_arr[] ={3,2,4,1,5};
vector<int> nums(nums_arr,nums_arr+5);
//默认建立大顶堆
cout<<"(default) make_heap:"<//建立大顶堆
cout<<"(less) make_heap:"<int>());
printVec(nums);
//建立小顶堆
cout<<"(greater) make_heap:"<int>());
printVec(nums);
return 0;
}
输出:
(default) make_heap:
5 3 4 1 2
(less) make_heap:
5 3 4 1 2
(greater) make_heap:
1 2 4 3 5
这里建立一个大顶堆,之后对在大顶堆上插入元素、删除元素。
//建立大顶堆
cout<<"(less) make_heap:"<int>());
printVec(nums);
//加入新数据 先在容器中加入,再调用push_heap()
nums.push_back(6);
push_heap(nums.begin(),nums.end(),less<int>());
cout<<"插入元素6"<//弹出堆顶元素,进行pop_back()操作
pop_heap(nums.begin(),nums.end(),less<int>());
cout<<" 删除堆顶元素前"<cout<<" 删除堆顶元素后"<
输出:
(less) make_heap:
5 3 4 1 2
插入元素6
6 3 5 1 2 4
删除堆顶元素前
5 3 4 1 2 6
删除堆顶元素后
5 3 4 1 2
通过输出可以验证之前的一句话:
pop_heap()并没有删除元素,而是将堆顶元素和数组最后一个元素进行了替换,如果要删除这个元素,还需要对数组进行pop_back()操作。
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
最大堆 | 最小堆
我们将数据分为两部分,位于左边最大堆的数据比右边最小堆的数据要小,左、右两边内部的数据没有排序,也可以根据左边最大的数及右边最小的数得到中位数。
接下来考虑用最大堆和最小堆实现的一些细节。
首先要保证数据平均分配到两个堆中,因此两个堆中数据的数目之差不能超过1.为了实现平均分配,可以在数据的总数目是偶数时把新数据插入到最小堆中,否则插入到最大堆中。
此外,还要保证最大堆中所有数据小于最小堆中数据。所以,新传入的数据需要先和最大堆的最大值或者最小堆中的最小值进行比较。
以总数目为偶数为例,按照我们制定的规则,新的数据会被插入到最小堆中,但是在这之前,我们需要判断这个数据和最大堆中的最大值谁更大,如果最大堆中的数据比较大,那么我们就需要把当前数据插入最大堆,然后弹出新的最大值,再插入到最小堆中。由于最终插入到最小堆的数字是原最大堆中最大的数字,这样就保证了最小堆中所有数字都大于最大堆的数字。
class Solution {
public:
void Insert(int num)
{
int size = max.size() + min.size();
if ((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>());
}
}
double GetMedian()
{
int size = max.size() + min.size();
if (size <= 0) return -1;
double median = 0;
if ((size & 1) != 0)
{
median = (double)min[0];
}
else
{
median = (double)(max[0] + min[0]) / 2;
}
return median;
}
vector<int> max; //数组中的前一半元素组成大顶堆
vector<int> min; //数组中的后一半元素组成小顶堆
};