您的位置:首页 > 其它

堆的实现、堆排序、优先队列

2017-09-22 00:12 302 查看

一、小根堆的实现、第一种堆排序

输入一组数据;

创建一个小根堆:从最后一个非叶子结点到第1个结点依次进行向下调整;

新增一个值:将新元素插入到末尾,再根据情况判断新元素是否需要上移,直到满足堆的特征为止;

【第一种堆排序】从小到大排序:对于小根堆,每次将顶部元素输出并删除顶部元素(最后一个元素移至顶部,堆的大小减1,继续向下调整),直到堆空。

测试示例

14

99 5 36 7 22 17 46 12 2 19 25 28 1 92

Y

55

#include <stdio.h>

int h[101];//用来存放堆的数组
int n;//用来存储堆中元素的个数,也就是堆的大小

//交换函数,用来交换堆中的两个元素的值
void swap(int x,int y){
int t=h[x];
h[x]=h[y];
h[y]=t;
}

//向下调整函数
void siftdown(int i){//传入一个需要向下调整的结点编号i.这里传入1,即从堆的定点开始向下调整
int t,flag=0;//flag用来标记是否需要继续向下调整
//当i结点有儿子(至少有左儿子)并且有需要继续调整的时候循环就执行
while(i*2<=n&&flag==0){
//首先判断它和左儿子的关系,并且用t记录较小的结点编号
if(h[i]>h[i*2])
t=i*2;
else
t=i;
//如果它有右儿子,再对右儿子进行讨论
if(i*2+1<=n)
if(h[i*2+1]<h[t])
t=i*2+1;
//如果最小的结点编号不是自己,说明子结点有比编号比父结点小的
if(t!=i){
swap(t,i); //交换它们
i=t;//更新i为刚才与它交换的儿子结点的编号,以便继续向下调整
}
else
flag=1;//否则说明当前的父结点已经比两个子结点都要小了了,不需要继续调整了
}
}

void siftup(int i){//传入一个需要向上调整的结点编号i
int flag=0;//用来标记是否需要继续向上调整
if(i==1)    return;//如果是堆顶就返回
//不是堆顶,并且当前结点比父结点小就继续向上调整
while(i!=1&&flag==0){
//判断是否比父结点小
if(h[i]<h[i/2])
swap(i,i/2);//交换它和它爸爸的位置
else
flag=1;//表示已经不需要继续调整了
i=i/2;//更新编号i为它父结点的编号,从而便于下一次继续向上调整
}
}

//建立堆的函数
void creat(){
int i;
//从最后一个非叶子结点到第1个结点依次进行向下调整
for(i=n/2;i>=1;i--)
siftdown(i);
}

//删除最大的数
int deletemax(){
int t=h[1];//用一个临时变量记录堆顶点的值
h[1]=h
;//将堆的最后一个点赋值到堆顶
n--;//堆的元素减少1;
siftdown(1);//向下调整
return t;//返回之前记录的堆的顶点的最小值
}
int main()
{
int i,num;
//读入要排序的数字的个数
scanf("%d",&num);
n=num;
for(i=1;i<=num;i++)
scanf("%d",&h[i]);
//建堆
creat();
//增加一个新元素
printf("是否增加一个新元素(Y/N): ");
char ch;
getchar();
scanf("%c",&ch);
if(ch=='Y'){
num++;n++;
scanf("%d",&h
);
siftup(n);
}
//删除顶部元素,连续删除n次,其实就是从小到大把数输出来
printf("排序后:\n");
for(i=1;i<=num;i++)
printf("%d ",deletemax());
return 0;
}


二、最大堆、第二种堆排序

输入一组数据;

创建一个大根堆:从最后一个非叶子结点到第1个结点依次进行向下调整;

【第二种排序】从小到大排序:建立大根堆,因为希望从小到大排序,所以最大的放在最后。每次将h[1]和h
交换,堆大小减1(n–),然后h[1]向下调整,直至堆的大小为1。

测试示例

14

99 5 36 7 22 17 46 12 2 19 25 28 1 92

#include <stdio.h>

int h[101];//用来存放堆的数组
int n;//用来存储堆中元素的个数,也就是堆的大小

//交换函数,用来交换堆中的两个元素的值
void swap(int x,int y){
int t=h[x];
h[x]=h[y];
h[y]=t;
}

//向下调整函数
void siftdown(int i){//传入一个需要向下调整的结点编号i.这里传入1,即从堆的定点开始向下调整
int t,flag=0;//flag用来标记是否需要继续向下调整
//当i结点有儿子(至少有左儿子)并且有需要继续调整的时候循环就执行
while(i*2<=n&&flag==0){
//首先判断它和左儿子的关系,并且用t记录较大的结点编号
if(h[i]<h[i*2])
t=i*2;
else
t=i;
//如果它有右儿子,再对右儿子进行讨论
if(i*2+1<=n)
if(h[i*2+1]>h[t])
t=i*2+1;
//如果最小的结点编号不是自己,说明子结点有比编号比父结点大的
if(t!=i){
swap(t,i); //交换它们
i=t;//更新i为刚才与它交换的儿子结点的编号,以便继续向下调整
}
else
flag=1;//否则说明当前的父结点已经比两个子结点都要大了,不需要继续调整了
}
}

//建立堆的函数
void creat(){
int i;
//从最后一个非叶子结点到第1个结点依次进行向下调整
for(i=n/2;i>=1;i--)
siftdown(i);
}

//堆排序
void heapsort(){
while(n>1){
swap(1,n);
n--;
siftdown(1);
}
}

int main()
{
int i,num;
//读入要排序的数字的个数
scanf("%d",&num);
n=num;
for(i=1;i<=num;i++)
scanf("%d",&h[i]);
//建堆
creat();
//堆排序
heapsort();
//输出
printf("排序后:\n");
for(i=1;i<=num;i++)
printf("%d ",h[i]);
return 0;
}


三、优先队列

像这样支持插入元素和寻找最大(小)值元素的数据结构称为优先队列。而堆就是优先队列的实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: