前言:
我们先来学习一下不用STL算法里的Heap函数,自己如何去实现一个堆?要明白,堆其实就是二叉树排序,跟树是有关系的。
知识点:
1、堆是一颗完全二叉树;
2、堆中的某个结点的值总是大于等于(最大堆)或小于等于(最小堆)其孩子结点的值。
3、堆中每个结点的子树都是堆树。
如下图(是个小根堆)
大根堆
构造代码:
第一步:先把堆构造出来`
//第一种方法就是边读入边构造
//建立小根堆
void siwfup(int i,int k){
whiel(i){
if(a[i]<a[i/2]){
swap(a[i],a[i/2]);
}
else break;
i = i/2;
}
void inio(){
int t;
cin>>t;
for(int i=0;i<t;++i){
int k;
cin>>k;
siwfup(i,k);
}
reutrn 0;
}
//第二种比较简单,直接存入数组,读取完后才调整
void swifdown(int i){
int k; //k变量用来存小值
while(i*2>t){ //首先判断是否有左边的儿子
if(a[i]>a[i*2]) k = i*2;
else k = i;
if(i*2+1>t && a[i*2+1]<a[k]) k = i*2+1; //再判断右边儿子
if(k==i) break;
else{
swap(a[i],a[k]);
i = k;
}
}
}
void inio(){
int t; cin>>t;
for(int i=0;i<t;++i){
cin>>a[i];
}
for(int i=t/2;i>=0;--i){
swifdown(i);
}
return ;
}
第二步:输出待排序的数值,也就是删除节点,输出节点的操作,其实也就是把头和尾相互调换,然后重新调整为小根堆;
void print(int t){
int n = t;
for(int i=0;i<n;++i){
cout<<a[0]<<' ';
swap(a[0],a[t]);
--t;
swifdown(0);
}
}
额外一步:(有时还可能继续添加元素)
void add(int i,int x){ //i是下标,x是元素
siwfup(i,x);
return ;
}
——————————————————————————
分割线
STL库:
- make_heap();
- push_heap();
- pop_heap();
- sort_heap();
make_heap
STL中的通过make_heap创建的堆,默认是大顶堆(max heap),即每个非叶子节点元素的值均不”小于”(默认使用<作为比较准则)其左右孩子节点。要改变堆的建立准则,可以自己制定一个比较函数.
// 1
template< class RandomIt >
void make_heap( RandomIt first, RandomIt last );
// 2
template< class RandomIt, class Compare >
void make_heap( RandomIt first, RandomIt last, Compare comp );
示例代码:
(默认的大根堆,make_heap需使用随机迭代器来创建)
vector<int> vi{8, 3, 4, 7, 5, 6};
make_heap(vi.begin(), vi.end());
for(auto i:vi){
cout<<i<<' ';
} // vi: 8 7 6 3 5 4
~
(自己写比较函数)
bool cmp(int a,int b){
return a<b;
}
vector<int> vi{8, 3, 4, 7, 5, 6};
make_heap(vi.begin(), vi.end(),cmp);
for(auto i:vi){
cout<<i<<' ';
} // vi: 3 5 4 7 8 6
push_heap
// 1
template< class RandomIt >
void push_heap( RandomIt first, RandomIt last );
// 2
template< class RandomIt, class Compare >
void push_heap( RandomIt first, RandomIt last, Compare comp );
调用push_heap之前必须调用make_heap创建一个堆
首先数组push_back插入元素,然后再调用push_heap,它会使最后一个元素插到合适位置
注意,push_heap中的_Compare和make_heap中的_Compare参数必须是一致的,不然会插入堆失败,最后一个元素还是在最后位置,导致插入失败
vector<int> vi{8, 3, 4, 7, 5, 6};
make_heap(vi.begin(), vi.end());
vi.push_back(9);
push_heap(vi.begin(), vi.end());
for(auto i:vi){
cout<<i<<' ';
} // vi: 9 8 7 6 3 5 4
~
pop_heap
它会将堆顶元素(数组第一个位置)和数组最后一个位置对调,然后你可以调用数组pop_back,删除这个元素
注意,pop_heap中的_Compare和make_heap中的_Compare参数必须是一致的,不然会失败
// 1
template< class RandomIt >
void pop_heap( RandomIt first, RandomIt last );
// 2
template< class RandomIt, class Compare >
void pop_heap( RandomIt first, RandomIt last, Compare comp );
示例
vector<int> v1{6, 1, 2, 5, 3, 4};
make_heap(v1.begin(), v1.end()); //6 5 4 1 3 2
pop_heap(v1.begin(), v1.end()); //5 3 4 1 2 6
v1.pop_back();
for(auto i:v1) cout<<i<<' '; // 5 3 4 1 2
~
sort_heap
sort_heap就是经典的堆排序算法,通过每次弹出堆顶直到堆为空,依次被弹出的元素就组成了有序的序列了。也可以想到应该是多次调用pop_heap()进行调整的。
// 1
template< class RandomIt >
void sort_heap( RandomIt first, RandomIt last );
// 2
template< class RandomIt, class Compare >
void sort_heap( RandomIt first, RandomIt last, Compare comp );
~
示例
vector<int> vi{8, 3, 4, 7, 5, 6};
make_heap(vi.begin(), vi.end());
sort_heap(vi.begin(),vi.end());
for(auto i:vi){
cout<<i<<' ';
} // vi: 3 4 5 6 7 8