堆与堆排序

来源:互联网 发布:db数据库修改器 编辑:程序博客网 时间:2024/04/29 05:19

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

二叉堆满足二个特性:

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;} 


原创粉丝点击