堆分为最小堆和最大堆,堆其实就是一棵特殊的完全二叉树,最小堆得特点是,所有的父节点都比子节点的数值要小,最大堆则就是所有的父节点都比子节点的数值都要大,所以取最小堆(最大堆)的最小值(最大值),都是取根节点的数值,插入数据呢,都是在完全二叉树最深层和最右侧的那个节点下一位置插入,当删除最值或插入新值的时候,都会破坏这种特殊的特性,所以我们要对此进行特殊的调整
有两种调整,一种是向上调整,即从下面的结点向上调整,向下调整则是从根节点向下进行调整。
看代码理解
//向上调整
void siftup(int i){
int flag = 1;
while(i != 1 && flag){
if(jk[i] < jk[i/2])
swap(jk[i], jk[i/2]);
else flag = 0;
i /= 2;
}
}
//向下调整
void siftdown(int i){
int t, flag = 1;
while(i*2 <= n && flag){
if(jk[i*2] < jk[i]) t = i*2;
else t = i;
if(i*2+1 <= n && jk[i*2+1] < jk[t])
t = i*2+1;
if(t == i) flag = 0;
else {
swap(jk[t], jk[i]);
i = t;
}
}
}
在进行删除根节点即取出最小值或者最大值的时候,则进行下面操作即可维持回堆的特性
int pop(){
int t = jk[1];
jk[1] = jk[n];
--n;
siftdown(1); //进行一次向下调整
return t;
}
建堆的时候其实就是不断地向下调整,我们都知道叶节点没有子女,因此我们从第一个非叶结点开始进行调整即可,第一个非叶结点为n/2。
for(i = n/2; i >= 1; --i)
siftdown(i);
而进行插入呢,则是从末尾加上然后进行一次向上调整即可。
void insert(int k){
++n;
jk[n] = k;
siftup(n);
}
完整代码如下,
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
int n, jk[105];
//向上调整
void siftup(int i){
int flag = 1;
while(i != 1 && flag){
if(jk[i] < jk[i/2])
swap(jk[i], jk[i/2]);
else flag = 0;
i /= 2;
}
}
//向下调整
void siftdown(int i){
int t, flag = 1;
while(i*2 <= n && flag){
if(jk[i*2] < jk[i]) t = i*2;
else t = i;
if(i*2+1 <= n && jk[i*2+1] < jk[t])
t = i*2+1;
if(t == i) flag = 0;
else {
swap(jk[t], jk[i]);
i = t;
}
}
}
int pop(){
int t = jk[1];
jk[1] = jk[n];
--n;
siftdown(1); //进行一次向下调整
return t;
}
void insert(int k){
++n;
jk[n] = k;
siftup(n);
}
int main(){
int i, t, k;
cin >> n;
for(i = 1; i <= n; ++i)
cin >> jk[i];
for(i = n/2; i >= 1; --i)
siftdown(i);
cin >> k;
insert(k);
while(n){
t = pop();
printf("%d ", t);
}
return 0;
}
当然,除了自行定义堆之外,c++还提供了一套优先队列模板,在优先队列中,优先级高的先出队
先贴一下queue的基本操作
1. queue队列的基本操作:
q.push(); 在队尾加入一个元素
q.front(); 返回队头元素(最早插入的元素)
q.back(); 返回队尾元素(最晚插入的元素)
q.pop(); 删除队头元素,无返回值
q.size(); 返回队列中元素的个数
q.empty(); 判断队列是否为空,真则返回true,假则返回false
2. priority_queue优先队列的基本操作:
q.push();加入一个元素(位置由其优先级确定)
q.top() 返回队列中优先级最高的元素
q.pop() 删除队列中优先级最高的元素,无返回值
q.size() 返回队列中元素的个数
q.empty() 判断队列是否为空,真则返回true,假则返回false
优先队列中无q.back()操作。
优先队列主要有三种用法,
1.
标准库默认使用元素类型的<操作符来确定它们之间的优先级关系。
优先队列的第一种用法,也是最常用的用法:
priority_queue<int> q1;
2.
第二种方法:
这时我们可以传入一个比较函数,使用functional.h函数对象作为比较函数。
priority_queue<int, vector<int>, greater<int> >q2;
其中,
第一个参数为元素类型。
第二个参数为容器类型。
第三个参数为比较函数。
3.
第三种方法:
自定义优先级。
struct node
{
friend bool operator< (node n1, node n2)
{
return n1.priority > n2.priority;
}
int priority;
int value;
};
或者,
struct node
{
bool operator< (node n) const
{
return this->priority > n.priority;
}
int priority;
int value;
};
在该结构中,value为值,priority为优先级。
通过自定义operator< 操作符来比较元素中的优先级。
但如果将operator< 操作符改为operator> 则会编译不过(G++编译器)
因为标准库默认使用元素类型的<操作符来确定它们之间的优先级关系。
而且自定义类型的<操作符与>操作符并无直接联系,故会编译不过。
这里在介绍一种快速插入节点的方法,重写构造函数,
看代码,
struct node2
{
int priority;
int value;
node2(int a = 0, int b = 0){ //重写构造函数,若不传入参数,默认值为0 0
priority = a;
value = b;
}
bool operator< (node2 a) const{
return this->priority > a.priority;
}
};
掌握这套模板,以后写优先队列会方便很多,
啰嗦一点,先来一份普通插入节点的代码
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
struct node
{
int priority;
int value;
friend bool operator< (node n1, node n2)
{
return n1.priority > n2.priority;
}
};
int main(){
int i;
priority_queue <node> q;
node b[10];
b[0].priority = 6; b[0].value = 1;
b[1].priority = 9; b[1].value = 5;
b[2].priority = 2; b[2].value = 3;
b[3].priority = 8; b[3].value = 2;
b[4].priority = 1; b[4].value = 4;
for(i = 0; i < 5; i++)
q.push(b[i]);
while(!q.empty()){
node t = q.top();
printf("%d %d\n", t.value, t.priority);
q.pop();
}
return 0;
}
重写构造函数之后,代码相对简洁一些。
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
struct node
{
int priority;
int value;
node(int a = 0, int b = 0){
priority = a;
value = b;
}
friend bool operator< (const node n1, const node n2) //加不加const无所谓
{
return n1.priority > n2.priority;
}
};
//或者
struct node2
{
int priority;
int value;
node2(int a = 0, int b = 0){
priority = a;
value = b;
}
bool operator< (node2 a) const{ //这个const必须加
return this->priority > a.priority;
}
};
int main(){
int i;
priority_queue <node> q;
q.push(node(6, 1));
q.push(node(9, 5));
q.push(node(2, 3));
q.push(node(8, 2));
q.push(node(1, 4));
q.push(node());
while(!q.empty()){
node t = q.top();
printf("%d %d\n", t.value, t.priority);
q.pop();
}
return 0;
}
加油~