堆的自行实现和c++优先队列模板

堆分为最小堆和最大堆,堆其实就是一棵特殊的完全二叉树,最小堆得特点是,所有的父节点都比子节点的数值要小,最大堆则就是所有的父节点都比子节点的数值都要大,所以取最小堆(最大堆)的最小值(最大值),都是取根节点的数值,插入数据呢,都是在完全二叉树最深层和最右侧的那个节点下一位置插入,当删除最值或插入新值的时候,都会破坏这种特殊的特性,所以我们要对此进行特殊的调整

有两种调整,一种是向上调整,即从下面的结点向上调整,向下调整则是从根节点向下进行调整。

看代码理解

//向上调整
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;
}

加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值