在学习堆排序之前首先了解一下二叉堆的特性:
1、二叉堆的父节点的值总是大于等于(或小于等于)其左右孩子的值;
2、每个节点的左右子树都是一棵这样的二叉堆。
如果该二叉堆的父节点总是大于孩子节点,则叫做最大堆,如果父节点小于孩子节点,则叫做最小堆。
在堆排序的应用中,如果递增排序,则应该使用最大堆,反之,使用最小堆。
堆排序是不稳定的
堆排序主要有两个步骤来完成堆排序:
1、把一个无序数列构造成一个最大堆或最小堆;
2、去掉根节点(堆顶元素),把剩下的元素重新构造一个二叉堆。
二叉堆的存储结构为一维数组,逻辑结构为二叉树。
在一个索引以0为开头的数组中,i为二叉堆的非叶子节点,它的左孩子为2*i+1,右孩子为2*i+2 ,他的父节点为(i-1)/2。
构造二叉堆:
1、选取最后一个非叶子节点,和他的左右孩子比较,如果有大于(小于)父节点的,则把左右孩子节点中较大的一个与父节点对换。
2、重复上述步骤,直到所有的节点全部扫描一遍,这样,二叉堆就构造好了。
调整二叉堆:
1、把堆顶元素和最后一个元素互换,构成了一个新的数组 a[0...n-1];
2、把新构成二叉堆重新扫描,因为只是更换了堆顶元素,所以只要扫描堆顶元素就可以了,完成后,构成了一个新的二叉堆。
3、重复上述步骤,直到二叉堆内的元素为1;
4、最后,新构成的数组就是排好序的了。
代码如下
#include<cstdio> void HeapadjustDown(int a[],int _begin,int _end){ //二叉堆构造函数 int temp=a[_begin]; //把节点保存在临时temp里 int lchild=2*_begin+1; //lchild为左孩子 while(lchild<_end){ //循环控制条件 if(lchild+1<_end&&a[lchild+1]>a[lchild]){ //从左右孩子中选取最大的 lchild++; } if(a[lchild]<temp){ //如果左右孩子节点都比父节点小,则退出 break; } a[_begin]=a[lchild];//父节点被子节点替换 _begin=lchild;//把当前父节点设置为替换的孩子节点 lchild=2*_begin+1;//重新设置左孩子节点 } a[_begin]=temp;//把最开始的父节点赋值到,孩子节点中。 } void HeapSort(int a[],int n){ for(int i=(n-1)/2;i>=0;i--){//这个循环是构造二叉堆 HeapadjustDown(a,i,n); } for(int i=n-1;i>=0;i--){//这个循环是构造有序数组 int temp=a[i]; a[i]=a[0]; a[0]=temp; HeapadjustDown(a,0,i);//去掉堆顶元素后,重新构造二叉堆 } } int main(){ int n,a[20]; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } HeapSort(a,n); for(int i=0;i<n;i++){ printf("%d ",a[i]); } }
时间: 2024-10-07 21:48:05