要讨论堆排序,首先就要简单的讨论一下堆这种数据结构.
堆排序所用到的二叉堆可以用数组表示出来,而且它可以被看作是一棵完全二叉树.出了树的最后一层,其它层应当是被填满的.
每个节点除了最后一层的叶子节点,从不同的角度观察都具备了不同的角色.假设有一个节点i,我们应当注意它产生的三个不同的角色.
Parent(i) = i / 2;
LeftChild(2i);
RightChild(2i + 1);
堆结构应当满足的性质是:
A[Parent(i)] > A[i]
既子节点最多只能等于父节点,而不能大于父节点.所以当有节点违反这个性质的时候就产生了一种调整的方法,使其符合堆的性质.过程就是查看节点i的LeftChild(i)和RightChild(i)是否大于节点i,如果违反了堆的性质,则把节点i和其较大的子节点交换,然后再递归的查看交换过的节点是否又违反了堆的性质,然后不停的调整,直到堆中所有的节点都没有违反堆的性质.
完成以上的步骤以后,整个数组中的最大值就位于了根节点的位置上,一个大根堆就这样建成了.接下来我们就可以用这个大根堆来进行堆排序了.
堆排序:
算法:
当一个大根堆建好的时候,数组中最大的节点就位于了A[1]的位置,既根节点的位置,我们把这个最大值和堆中的最后一个节点A[n]交换,并把A[n]从堆中去掉,将堆的规模减小1.堆排序的一步就完成了.同时因为交换上去的节点再一次的破坏了堆的性质,我们再从新的建立一个大根堆,完成以后,数据数组中的次大元素就位于了根节点的位置,接下来继续对它进行刚才最大元素所经历的操作,照此循环往复,就可以完成堆排序了.完成的时候,数组的第一个节点就是数据中最小的节点,最后一个是最大的节点.
代码:
#include
<
stdio.h
>
#define
Parent(i) i / 2
#define
Left(i) 2 * i
#define
Right(i) (2 * i + 1)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
const
int
MAX
=
11
;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
void
Heapify(
int
*
A,
int
i,
int
heapSize)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
int left, right, parent, largest, temp, done;
done = 0;
largest = -1;
while(Parent(i) <= heapSize && i >= Parent(i))
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
left = Left(i);
right = Right(i);
parent = Parent(i);
if(left < heapSize && A[left] > A[i])
largest = left;
else
largest = i;
if(right < heapSize && A[right] > A[largest])
largest = right;
if(largest != i)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
temp = A[largest];
A[largest] = A[i];
A[i] = temp;
}
i++;
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
void
buildHeap(
int
*
A,
int
heapSize)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
int i;
for(i = (MAX - 1) / 2; i > 1; i--)
Heapify(A, i, heapSize);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
void
heapSort(
int
*
A)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
int i, temp, heapSize;
heapSize = MAX - 1;
buildHeap(A, heapSize);
for(i = MAX - 1; i >= 1; i--)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
temp = A[i];
A[i] = A[1];
A[1] = temp;
heapSize -= 1;
Heapify(A, 1, heapSize);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
int
main()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
int A[MAX];
int i;
int nums = MAX - 1;
printf("Please input %d numbers: ", nums);
for(i = 1; i < MAX; i++)
scanf("%d", &A[i]);
heapSort(A);
for(i = 1; i < MAX; i++)
printf("%d ", A[i]);
return 0;
}
堆排序的时间复杂度的上界是O(nlog2n),现在还没有明确的下界.不过实践中堆排序要比使用Sedgewick增量序列的希尔排序慢.并且因为需要不停的建堆,所以元素较少的时候不推荐用堆排序.