堆及堆的应用/单调队列/堆排序

堆一般分为大根堆和小根队,堆形象说是一棵完全二叉树;用数组实现堆时我一般将根节点从1开始,父节点编号为i,则左儿子节点编号为2i,右儿子编号为2i+1;堆的两个基本函数:插入元素到堆中(在此过程中建立好了初始堆),从堆中取出堆顶元素删除;实现堆的代码以具体题目写出;

第九次作业:

1、
(每次取出堆中data最大的节点然后输出name之后删除,堆空时输出Sleep)题目链接

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define MAX_SIZE 100005
///*以下是以题目所要建堆类型的函数代码*/ 

typedef struct anode* Node;
typedef struct anode
{
	char name[50];
	int data;
	
}Anode;
int lenth=0;							//先定义堆的大小为0; 
Anode heap[MAX_SIZE];

void Insert_Heap(Anode p)				//将一个节点插入到堆中,不论此时堆是否为空; 
{
//	/*此时将已存在的堆视为已经是一个标准堆*/
//	/*此时树父节点时i(1开始),左儿子节点是2*i,右儿子是2*i+1;*/
	
	int i=lenth;						//插入数据之后的堆的大小,初始i为堆的最大编号,此时该位置未置入值; 
	while(i!=1&&p.data>heap[i/2].data)//假设p先插入到i位置,从i开始向上比较是否比父节点大,如果大,则交换值,并继续向上移动比较; 
	{
		heap[i]=heap[i/2];
		i/=2;
	}
	heap[i]=p;							//1、插入堆的第一个元素;2、循环终止之后到达一个比父节点键值小的节点,将p插入储存到该节点; 
}

void Delete_Heap()						//无需传参数,因为每次都是将堆的最大顶即边界删除; 
{
//	/*将最大顶heap[1]原键值删除,将堆的最大编号键值插入到最大顶中,然后对堆进行重新调整;即交换头尾键值,然后对lenth-1大小的堆调整;*/ 
	heap[1]=heap[lenth];
	lenth--;
	Anode tmp=heap[1];
	int num=heap[1].data;
	int fa=1,son=2;						//最开始1是根(父)节点 ,每次先将son设为左儿子; 
	while(fa<=lenth&&son<=lenth)
	{
		if(son<lenth&&heap[son].data<heap[son+1].data)son++;	//判断左儿子和右儿子大小,因为需要将交换后的根节点以下的最大值找出置入堆顶,所以判断更大值; 
		if(num>heap[son].data)break;	//num(交换值)比该节点值大,则um位置找出,终止循环; 
//		/*还未找到num位置,则继续向下寻找,此时通过更替fa,son的值向下递进;*/
		heap[fa]=heap[son];
		fa=son;
		son*=2;
	}
	heap[fa]=tmp;						//即寻找到交换节点的位置之后,将其存储置入;	 
}

int main()
{
	int m,i,j;
	scanf("%d",&m);
	for(i=1;i<=m;i++)
	{
		char oper[10];
		scanf("%s",&oper);
		if(oper[0]=='G')
		{
			if(lenth==0)printf("Sleep!
");
			else
			{
				printf("%s
",heap[1].name);
				Delete_Heap();
			}
		}
		else if(oper[0]=='P')
		{
			lenth++;						//需要插入一个节点,先使堆大小增加,类似开辟好一个节点存储将输入的节点 ; 
			scanf("%s %d",&heap[lenth].name,&heap[lenth].data);
			Insert_Heap(heap[lenth]); 
		}
	}
	return 0;
}

2、(合并多堆石头,求总共花费体力值最少)题目链接

#include<cstdio>
#include<cstdlib>
#include<algorithm>
int sum=0;
int lenth=0;
int stons[100005];
void Insert(int ston)
{
	int i=lenth;
	while(i!=1&&ston<stons[i/2])
	{
		stons[i]=stons[i/2];
		i/=2;
	}
	stons[i]=ston;
}
void Delete()
{
	int fa=1,son=2;
	stons[1]=stons[lenth--];
	int tmp=stons[1];
	while(fa<=lenth&&son<=lenth)
	{
		if(son<lenth&&stons[son]>stons[son+1])son++;
		if(tmp<stons[son])break;
		stons[fa]=stons[son];
		fa=son;
		son*=2;
	}
	stons[fa]=tmp;
}

int main()
{
	int n,i,j;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		lenth++;
		scanf("%d",&stons[lenth]);
		Insert(stons[lenth]);
	}
	while(lenth>0)
	{
		int tmp1=stons[1];		Delete();
		int tmp2=stons[1];		Delete();
		sum+=tmp1+tmp2;
//		printf("%d %d %d
",tmp1,tmp2,sum);
		if(lenth==0)break;
		lenth++;stons[lenth]=tmp1+tmp2;
		Insert(stons[lenth]);
	}
	printf("%d
",sum);
	return 0;
}

堆的实际应用:
1、优先队列(每次查询都是堆顶元素O(1),但是进队和出队每次都要重新调整堆O(nlogn)):首先可以直接通过建立堆实现优先队列,其次可以直接使用STL中的单调队列函数,需要包含文件,若需要使用已有的大小排序,可包含头文件;STL优先队列使用代码:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<iostream>
#include<string>
#include<vector>
using namespace std;

/*自定义比较结构,数据类型为整型*/
struct cmp1
{
	bool operator ()(int &a,int &b)
	{
		return a>b;						//最小值优先; 
	}
};
struct cmp2
{
	bool operator ()(int &a,int &b)
	{
		return a<b;						//最大值优先; 
	}
};

/*自定义数据结构*/
struct number
{
	int data;
	string name;
	bool operator < (const number &a)const
	{
		return data>a.data;				//最大值优先; 
	}
};

int a[]={12,35,7,9,25,16,75,2,14,99};
number num[]={16,"jim",11,"tom",48,"kate",40,"sam",5,"hat"};

int main()
{
	priority_queue<int>que;							//采用默认优先级后遭优先队列; 
	priority_queue<int,vector<int>,cmp1>que1; 		//采用cmp1的大小关系优先; 
	priority_queue<int,vector<int>,cmp2>que2;		//采用cmp2的大小关系优先; 
	priority_queue<number>que3;						//采用自定义数据类型number中的重载函数大小优先; 
	int i;
	for(i=0;a[i];i++)								//将a数组中的值进入队列que,que1,que2; 
	{
		que.push(a[i]);
		que1.push(a[i]);
		que2.push(a[i]);
	}
	for(i=0;num[i].data;i++)
	{
		que3.push(num[i]);
	}
	
	printf("默认优先关系输出:
");
	while(!que.empty())
	{
		printf("%d ",que.top());
		que.pop();
	}
	printf("


");
	
	printf("采用cmp1大小关系优先级输出:
");
	while(!que1.empty())
	{
		printf("%d ",que1.top());
		que1.pop(); 
	}
	printf("


");
	
	printf("采用cmp2大小关系优先级输出:
");
	while(!que2.empty())
	{
		printf("%d ",que2.top());
		que2.pop();
	}
	printf("


");
	
	printf("采用自定义数据类型重载函数优先级输出:
");
	while(!que3.empty())
	{
		cout<<que3.top().name<<" "<<que3.top().data<<endl;
		que3.pop();
	}
	cout<<endl<<endl<<endl;
	
	return 0; 
}

2、堆排序:堆排序在之前的排序博客中已经写出,博客链接

这里写到C语言中的结构体的重载函数写法和C++中的类似;后续详细复习补上;

原文地址:https://www.cnblogs.com/heihuifei/p/8233004.html