zoukankan      html  css  js  c++  java
  • 堆及堆的应用/单调队列/堆排序

    堆一般分为大根堆和小根队,堆形象说是一棵完全二叉树;用数组实现堆时我一般将根节点从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++中的类似;后续详细复习补上;

  • 相关阅读:
    java 泛型 -- 泛型类,泛型接口,泛型方法
    android:id="@id/resid" , andorid:id="@+id/resid" 的区别
    Ubuntu16.04 + Win 10 双系统 时间同步,启动项顺序,NumLock指示灯常亮
    Linux 下 Console / 控制台 复制粘贴快捷键
    Android 快速开发框架:推荐10个框架:afinal、ThinkAndroid、andBase、KJFrameForAndroid、SmartAndroid、dhroid..
    JQuery的Ajax跨域请求的
    1031. Hello World for U (20)
    项目进阶 之 集群环境搭建(三)多管理节点MySQL集群
    Tomcatserverhttps协议配置简单介绍
    Unity3D
  • 原文地址:https://www.cnblogs.com/heihuifei/p/8233004.html
Copyright © 2011-2022 走看看