堆排序
堆排序是基于堆的数据结构将待排序元素进行处理的一种排序
堆的一棵具有特殊性质的完全二叉树:该二叉树具有堆序性,即任何一个节点的值大于等于其孩子节点(或小于等于其孩子节点的值);如果任一节点的值大于孩子节点的值,这样的完全二叉树称为大根堆;如果任意节点的值小于等于其孩子节点的值,这样的完全二叉树称为小根堆。
基于上面的定义,不难得到堆一定具有这样的性质:堆顶元素一定是堆中最大或者最小的元素,所以我们可以不断输出堆顶元素,并时刻维护这个堆,就能保证我们每次输出的堆顶元素依次是待排序列中的最值。这样一来不断输出至堆空时,我们就得到了一组有序的序列。
堆排序的重点是对于堆的构建和维护。
堆的构建操作是指从n/2节点开始到1节点,逐次对节点进行调整,使以n/2…1节点为根的子堆保持有序。
而对堆进行的调整操作是指将根节点的值不断与子节点的值进行比较,直到得到一个合适的位置,其间不断交换子节点与父节点的值。
#include <stdio.h>
#include <stdlib.h>
//基于大根堆的堆排序
void Adjust(int [],int ,int);
//初始化大根堆
void BuildMaxHeap(int heap[],int n)
{
int root=n/2,i;
for(root;root>=1;root--)
{
Adjust(heap,root,n);
}
}
//调整root节点和子树的顺序(下滤)
void Adjust(int heap[],int root,int n)
{
heap[0]=heap[root];
int left,right;
int bigger_node=left;
for(root;root<=n/2;root=bigger_node)//将root部位的元素下滤到堆中合适的位置
{
left=root*2;
right=root*2+1;
bigger_node=left;
if(right<=n) bigger_node = heap[right]>heap[left] ? right : left ;//如果存在右孩子,则比较左右孩子中最大的
if(heap[0]<heap[bigger_node])
{
heap[root]=heap[bigger_node];
}
else //如果root位置的元素不小于孩子节点的元素,说明找到了最终合适的位置,跳出循环
{
bigger_node=root;//该位置即为最终位置
break;
}
}
heap[bigger_node]=heap[0];//将heap[0]保存的元素归为到最终位置
}
//堆排序
HeapSort(int heap[],int n)
{
BuildMaxHeap(heap,n);
int i,k=n;
for(i=1;i<=k;i++)
{
printf("%d ",heap[1]);//每次输入堆顶元素,即当前堆中的最大值
heap[1]=heap[n--];//将堆底元素移到堆顶并在下一步进行调整以保持堆序性
Adjust(heap,1,n);//将移动后的堆进行调整
}
}
main()
{
int heap[]={0,53,17,78,9,45,65,87,32};
int n=8;
HeapSort(heap,n);
}
堆排序是空间复杂度为O(1)
建造堆的时间复杂度为O(n)
每次调整堆的时间复杂度为O(h),其中h为完全二叉树的高度logn
在最坏和最好情况下堆排序的时间复杂度都是O(nlogn)
堆排序是一种不稳定排序