堆排序是一种利用二叉堆的性质来进行排序的算法,二叉堆本身可以使用一个数组来表示,若我们有一个无序数组,我们可以先将其通过一系列方法变成一个二叉堆,然后再进行出堆操作,实际上就是将其目前最大值或最小值输出,这样我们的输出顺序其实就是按照从小到大或者从大到小一个顺序,也就间接完成了我们的排序工作。
首先,二叉堆是什么呢?
第一,二叉堆首先是一颗完全二叉树,也就是一颗除了最底层其他层节点全部都是满的且最底层节点从左到右是满的,右边可以空缺这样一颗树。
第二,二叉堆与完全二叉树不同点在于二叉堆的根节点总是大于等于或小于等于其子节点的值。
知道了二叉堆的概念,那么我们接下来要了解一下为什么二叉堆是使用数组来存储的,这是因为假如使用链式存储的方式来存储的话,一是占用空间,二是实现复杂,由于完全二叉树的节点之间是没有空隔的,所以我们完全可以使用数组来存储它,这样并不浪费空间,而且对二叉堆的操作也转换成了对数组的操作,这样的操作也简便,所以使用数组来存储是非常合适的。
假如我们有一个二叉堆数组,那怎么才能将其元素们进行排序呢?对于二叉堆我们有一个方法来将最大或者最小元素进行输出,就是出堆操作,将堆顶元素进行出堆,这时二叉堆堆顶是空,我们这时将二叉堆最后一个元素放到堆顶,再通过一系列办法将这个堆重新变成二叉堆,再次出堆堆顶元素,以此往复,这样就能保证出堆元素一直是当前堆的最大元素或最小元素,这样我们就实现了排序的过程。
以下是C语言代码实现:
//交换数组中两个元素的位置,i,j为两个元素的index
void swap(int tree[], int i, int j)
{
int temp = tree[i];
tree[i] = tree[j];
tree[j] = temp;
}
//n为数组中元素个数 i为进行heapify算法的节点,同时也是堆的下沉操作
void heapify(int tree[], int n, int i)
{
int c1 = 2 * i + 1;
int c2 = 2 * i + 2;
int max = i;
if (tree[c1] > tree[max] && c1<n)
{
max = c1;
}
if (tree[c2] > tree[max] && c2<n)
{
max = c2;
}
if (max != i)
{
swap(tree, max, i);
heapify(tree, n, max);
}
}
//void Upheapify(int tree[], int n, int i)
//{
// int p = (i - 1) / 2;
// if (tree[p] < tree[i] && p>=0)
// {
// swap(tree, p, i);
// Upheapify(tree, n, p);
// }
//}
//使数组完全变成一个堆,n为元素个数
void build_heap(int tree[], int n)
{
int last_node = n - 1;
int parent = (last_node - 1) / 2;
for (int i = parent;i >= 0;i--)
{
heapify(tree,n,i);
}
}
//排序输出
void heap_Sort(int tree[],int n)
{
build_heap(tree, n);
int i;
for (i = n - 1;i >= 0;i--)
{
printf("%d\n", tree[0]);
swap(tree, i, 0);
heapify(tree, i, 0);
}
}
int main()
{
int tree[8] = { 12,1,22,34,15,33,45,35 };
heap_Sort(tree, 8);
}
以上代码我已经测试过可以直接使用
堆排序时间复杂度O (nlgn)