堆排序
概念:
堆是具有以下性质的完全二叉树:
1.每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;
2.每个结点的值都小于或等于其左 右孩子结点的值,称为小顶堆
同时,我们对堆中的结点按层从左至右进行编号(0,1...),将这种逻辑结构映射到数组中。
下标为i的节点的父节点下标:(i-1)/2
下标为i的节点的左孩子下标:i*2+1
下标为i的节点的右孩子下标:i*2+2
注意:树中存放的是数组元素的下标。
思路
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
(1)调整序列使其变成大顶堆:就是从下往上、从右到左,将每个非叶节点当作根节点,将其和子树调整成大顶堆;
(2)堆排序:就是先构建一个堆,用(1)对其调整为大顶堆;
(3)让调整好后的大顶堆的根节点和最后一个节点互换对应的数组中的值,并让最后的节点移走,再从编号为0的树调整序列构成大顶堆;
(4)重复(3)中步骤,直至数的长度小于1;
时间空间复杂度
时间复杂度:O(nlogn)
堆排序分为建堆和调整堆两大块。
建堆是通过父节点和子节点两两比较并交换得到的,时间复杂度为O(n),调整堆需要交换n-1次堆顶元素,并调整堆,调整堆的过程就是满二叉树的深度logn,时间复杂度为O(nlogn),
空间复杂度为O(1)。
核心代码实现
int heap_adjust(int *s,int len,int n)//构成大顶堆,s要排序的数组名,len 数组长度
{
if(s==NULL||len<0)
{
return -1;
}
int max=n;
int lchild=2*n+1;
int rchild=2*n+2;
if(s[max]<s[lchild]&&lchild<len)
{
max=lchild;
}
if(s[max]<s[rchild]&&rchild<len)
{
max=rchild;
}
if(max!=n)
{
swap(&s[max],&s[n]);
heap_adjust(s,len,max);
}
return 0;
}
int heap_sort(int *s,int len)//堆排序
{
int i=len/2-1;//树最右边的叶节点的父节点
for(;i>=0;i--)//从最右端开始比较交换,构成第一次构成大顶堆
{
heap_adjust(s,len,i);
}
for(i=len-1;i>=0;i--)//交换最大值与树的最右边子节点对应的数组数据,每执行一次,将长度-1,是最大值不再参与运算;之后在调用heap_adjust函数,构成新的大顶堆;
{
swap(&s[0].&s[i]);
heap_adjust(s,i,0);//从树的根开始调整交换,构成新的大顶堆;
}
return 0;
}
void swap(int *p,int *q)
{
int temp;
temp=*p;
*p=*q;
*q=temp;
}
void data_show(int *s,int len)
{
int i;
for(i=0;i<len;i++)
{
printf("%d ",s[i]);
}
printf("\n");
}
运行结果