heap sort

来源:互联网 发布:杨梅网络 编辑:程序博客网 时间:2024/05/20 22:01

 /*
 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.它是不稳定的排序方法。
 */

原创粉丝点击