堆与堆排序

二叉堆是完全二叉树或者是近似完全二叉树。

二叉堆满足二个特性:

1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。下图展示一个大根堆:

接下来是一个小根堆:



堆的操作——插入删除

下面先给出《数据结构C++语言描述》中最小堆的建立插入删除的图解

这样有什么好处呢?好处就是能方便地把指针省略掉,用一个简单的数组来表示一棵树,如图:

下面接着介绍一个维护方法:

假设要在这个二叉堆里入队一个单元,键值为2,那只需在数组末尾加入这个元素,然后尽可能把这个元素往上挪,直到挪不动,经过了这种复杂度为Ο(logn)的操作,二叉堆还是二叉堆。

那如何出队呢?也不难,看图:


堆排序:

一个动态的堆排序过程



最差时间复杂度O(nlogn)
最优时间复杂度O(nlogn)[1]
平均时间复杂度\Theta(nlog n)
最差空间复杂度O(n) total, O(1) auxiliary

下面是一个堆排序的代码:

#include <iostream>
#include <stdlib.h>
int data[2000],n,len;
FILE *fin,*fout; 
void change(int &a,int &b){ 
  //快速交换2个数
   a^=b;b^=a;a^=b;
}  
 
void sift(int a){//维护堆过程 
	int l=a*2;
	if (l>len) return ; 
	if (l+1<len && data[l+1]>data[l]) l++;//选取左右子节点中最大的一个 
	if (data[l]>data[a]) {//比较子节点与父节点 
		change(data[a],data[l]);
		sift(l);//维护子节点堆 
	}
}

void heap_sort(){
	//堆排序主过程 
	int i;len=n;
	for (i=n/2;i>0;i--)
	  sift(i);//维护以i为父节点的堆,将数据中最大的交换到data[1],即堆顶 
	for (i=1;i<=n;i++){
		change(data[1],data[len]);//交换堆顶和堆底元素 
		len--;//减少堆元素 
		sift(1); //整理堆 
	}
}
void input(){
	//读入元素 
	int i;
	fscanf(fin,"%d",&n);//表示读入的数为n 
	for (i=1;i<=n;i++)
	 fscanf(fin,"%d",&data[i]);//将n个数读入到data数组中,注意这里数组从1开始 
	fclose(fin);
}
void output(){
	//输出排序后结果 
	int i;
	for (i=1;i<=n;i++)
	  fprintf(fout,"%d ",data[i]); 
	fclose(fout);
}
int main(){
	fin=fopen("heapin.txt","r");
	fout=fopen("heapout.txt","w");
	input();
	heap_sort();
	output();
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值