算法:堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成-一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成-一个堆, 这样就会得到n个元素中的次小值。如此反复执行,便能得到-一个有序序列了。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAXSIZE 20
typedef int data_t;
typedef struct{
data_t data[MAXSIZE];
int last;
}Seqlist_t;
Seqlist_t *seqlist_create() //创建顺序表
{
Seqlist_t *S = (Seqlist_t *)malloc(sizeof(Seqlist_t));
if(S == NULL)
{
perror("malloc");
return NULL;
}
S->last = -1;
return S;
}
int seqlist_insert(Seqlist_t *L,data_t x,int pos) //在顺序表中插入数值
{
if(L == NULL)
return -1;
if(L->last == MAXSIZE-1 || pos<0 || pos>L->last+1)
return -1;
int i = 0;
for(i=L->last; i>=pos; i--)
L->data[i+1] = L->data[i];
L->data[pos] = x;
L->last += 1;
return 0;
}
//将堆顶记录和当前未经排序子序列的最后一个记录交换
void seqlist_swap(Seqlist_t *L, int i, int j)
{
int temp = L->data[i];
L->data[i] = L->data[j];
L->data[j] = temp;
}
//已知L->data[r. .l]中记录的关键字除L->data{r]之外均满足堆的定义
//本函数调整L->data[r]的关键字,使L->data[r..l]成为一个大顶堆
void seqlist_heapadjust(Seqlist_t *L,int r,int l)
{
int i = 0;
int temp = L->data[r];
for(i=2*r; i<=l; i=i*2) //沿关键字较大的孩子结点向下筛选
{
if(i<l && L->data[i]<L->data[i+1])
++i; //i为关键字中较大的记录的下标
if(temp >= L->data[i])
break; //data[i]应该插入在r位置上
L->data[r] = L->data[i];
r = i;
}
L->data[r] = temp; //插入
}
//堆排序,对顺序表进行
void seqlist_heapsort(Seqlist_t *L)
{
int i = 0;
for(i=(L->last+1)/2; i>0; i--) //把L中的data[i]构建成大顶堆
seqlist_heapadjust(L,i,(L->last+1));
for(i=(L->last+1)/2; i>1; i--)
{
seqlist_swap(L,1,i); 将堆顶记录和当前未经排序子序列的最后一个记录交换
seqlist_heapadjust(L,1,i-1); //将L->data[1..i-1]重新调整为大顶堆
}
}
void seqlist_show(Seqlist_t *L) //对顺序表进行遍历
{
if(L == NULL)
return ;
int i = 0;
for(i=0; i<=L->last; i++)
printf("%d ",L->data[i]);
printf("\n");
}
int seqlist_length(Seqlist_t *L) //求顺序表的长度
{
if(L == NULL)
return -1;
return (L->last+1);
}
int main(int argc, char *argv[])
{
Seqlist_t *S = seqlist_create();
srand(time(NULL));
int i = 0;
while(i < 15)
{ //生成随机数10-150进行排序
seqlist_insert(S,(i+1),rand()%(i+1));
i++;
}
printf("befor sort:\n");
seqlist_show(S);
seqlist_heapsort(S);
printf("after sort:\n");
seqlist_show(S);
return 0;
}
运行结果:
堆排序复杂度:
它的运行时间主要是消耗在初始构建堆和在重建堆时的反复筛选上。
在构建堆的过程中,因为我们是完全二又树从最下层最右边的非终端结点开始构建,将它与其孩子进行比较和若有必要的互换,对于每个非终端结点来说,其实最多进行两次比较和互换操作,因此整个构建堆的时间复杂度为0(n)。
在正式排序时,第i次取堆顶记录重建堆需要用0(ogi)的时间(完全二叉树的某个结点到根结点的距离为Llog2i]+1),并且需要取n-1次堆顶记录,因此,重建堆;的时间复杂度为0(nlogn)。