您的位置:首页 > 其它

堆排序

2012-04-24 14:27 155 查看
/*

ary是存储记录的数组, start是需要调整为大顶堆的根记录下标, end是

它的最后一个叶子记录的下标。

注意,传入的start到end之间的记录,除去根记录,根记录的左右子二叉树都

是大顶堆, 要完全符合大顶堆的性质调用此函数才有效。

下面函数要做的就是调整以start为根记录,end为最后一个叶子记录的完全二叉树为大顶堆。

*/

void Heapify(int ary[], unsigned int start, unsigned int end)

{

unsigned int left = 0;

unsigned int right = 0;

unsigned int max = 0;

left = start * 2;

right = start * 2 + 1;

/* 如果存在左子记录 */

while (left <= end)

{

/* 左右子记录中,先默认左子记录的关键字值最大,保存其下标 */

max = left;

/* 如果左右子记录都存在,改写max使其保存了拥有最大关键字值记录的下标 */

if (right <= end)

{

if (ary[left] < ary[right])

{

max = right;

}

else

{

max = left;

}

}

/* 现在max中是保存了左右子记录中(假设存在)关键字值最大的下标 */

if (ary[start] < ary[max])

{

/* 根记录的关键字值小于左右子记录中关键字值最大的 */

ary[0] = ary[max];

ary[max] = ary[start];

ary[start] = ary[0];

/* 从被交换的子记录中开始调整 */

start = max;

}

else

{

/* 二叉树符合大顶堆性质, 调整结束 */

break;

}

left = start * 2;

right = start * 2 + 1;

}

/* 调整到最二叉树的最后一个叶子结点上, 二叉树也符合大顶堆性质, 调整结束 */

}

/* 进行堆排序, 完成后ary中的记录下标从1到size,它们的关键字值是非递减的 */

void HeapSort(int ary[], unsigned int size)

{

/* 建堆 */

BuildHeap(ary, size);

for (unsigned int i = size; i > 0; i--)

{

/* 把根记录和最后一个叶子记录交换 */

ary[0] = ary[1];

ary[1] = ary[i];

ary[i] = ary[0];

/*

调整从1到i- 1组成的二叉树为大顶堆。

注意:

交换只是破坏了以ary【1】为根的二叉树大顶堆性质, 它的左右

子二叉树还是具 备大顶堆性质。

*/

Heapify(ary, 1, i - 1);

}

}

/*

调整整棵二叉树(完全无序状态)使之成为大顶堆。

策略:

首先这课二叉树的结构是完全二叉树, 我们可以从最后一个非叶子记录调整,

直到整棵二叉树的根记录。

比如二叉树有size个结点记录, 那么最后一个非叶子结点的下标就

是size / 2(取整), 我们就从size / 2, size / 2 - 1, ... ,

直到调整以1为下标的整棵二叉树为大顶堆。

因为以最后一个叶子记录为根的二叉树必定符合调用函数Heapify的条件,

位于同一层的非叶子结点必定也符合调用条件,Heapify函数处理完后这一层后,

上一层的非叶子记录对应的二叉树也符合了调用条件,

这样直到以整棵二叉树的根(即ary【1】)记录为根的二叉树也符合大顶堆的性质,

整个大顶堆就建立起来。

现在整棵二叉树的根记录就是该记录集中关键字值最大的记录。

*/

void BuildHeap(int ary[], unsigned int size)

{

/* 传入下标为i(size / 2 >= i >= 1)的记录为根,

下标为size的最后一个叶子记录的二叉树进行调整 */

for (unsigned int i = size / 2; i > 0; i--)

{

Heapify(ary, i, size);

}

}

/*

算法分析:

1.堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,

它们均是通过调用Heapify实现的。

 堆排序的最坏时间复杂度为O(n * lgn)。堆排序的平均性能较接近于最坏性能。

BuildHeap最坏情况下时间复杂度为O(n),但是for循环在最坏情况下却

要O(n * lgn)的时间。

2.由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。

3.堆排序是就地排序,辅助空间为O(1)。

4.它是不稳定的排序方法。

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: