选择排序之堆排序

1.堆排序算法:

         堆是一个顺序存储的完全二叉树,其中每个节点的关键字都大于或等于其孩子节点的关键字,这样的堆称为大根堆(从小到大排序)。如果是进行从大到小的排序,则堆中每个节点的关键字都小于或等于其孩子节点的关键字,这样的堆称为小根堆。

       (1)堆排序的基本思想想:

                   首先按照大根堆的定义将A[1...n]调整为堆(这个过程称为初始建堆),交换A[1]和A[n](将最大元素A[1]归位,放到排序序列的最后),然后,将A[1...n-1]调整为堆,交换A[1]和A[n-1];如此反复进行,直到交换了A[1]和A[2]为止。

                  在堆排序过程中多次调用筛选算法。筛选算法的前提是:将A[low...high]看成是一棵以A[low]为跟节点的完全二叉树,其左子树和右子树均为大根堆,只有跟节点不满足大根堆的条件。筛选算法用于将这样的完全二叉树调整成一个大根堆,其过程是:从根节点A[low](i=low)开始,从它的两个孩子A[low+1]和A[low+2]中比较选出关键字较大者,用j记录较大孩子的下标,若A[j].key>A[i].key,将A[i]、A[j]进行交换,然后从A[j]出发继续进行,如此直到树叶为止;若A[j].key>A[i].key不成立,则结束筛选过程。上述筛选过程可用算法Sift(A,low,high)实现。

           

void Sift(int a[],int low,int high)//a[low...high]调整成堆
{
     int i=low,j=2*i,t=a[i];//a[j]是a[i]的左孩子

     while (j <= high)
     {
         if (j<high && a[j]<a[j+1])//若右孩子较大,把j指向右孩子
         {
             j++;
         }

         if (t<a[j])//若双亲关键字小于,需要调整
         {
             a[i] = a[j];//将a[i]调整到双亲节点位置上
             i = j;//修改i和j的值,以便继续向下筛选
             j = 2*i;
         }
         else
         {
             break;//若双亲关键字大于两孩子关键字,筛选结束
         }
     }

     a[i] = t;//被筛选节点的值放入最终位置
}
 

                 初始建堆的工作可通过反复筛选来完成:从完全二叉树的最后一个分支节点A[i](其中i=n/2,i为最后一个分支节点的下标)开始,通过调用算法Sift(A,j,n)(其中j=i、i-1、...、1),依次将A[i]、A[i-1]、...、A[1]为根的子树调整为堆。堆排序算法如下。

void Heap_Sort(int a[],int n)
{
    int i,t;
    for (i=n/2; i>=1; i--)//循环建立初始堆
    {
       Sift(a,i,n);
    }

    for (i=n; i>=2; i--)//进行n-1次循环,完成堆排序
    {
        t = a[1];//将第一个元素同当前区间内的a[1]对换
        a[1] = a[i];
        a[i] = t;
        Sift(a,1,i-1);//筛选a[1]节点,得到i-1个节点的堆
    }
}

2.例如:

  先输入元素个数n,接着再输入n个元素,用堆排序将这n个数升序排序。

3.代码示例:

  

#include <iostream>
#define MaxSize 50
using namespace std;

void Sift(int a[],int low,int high)//a[low...high]调整成堆
{
     int i=low,j=2*i,t=a[i];//a[j]是a[i]的左孩子

     while (j <= high)
     {
         if (j<high && a[j]<a[j+1])//若右孩子较大,把j指向右孩子
         {
             j++;
         }

         if (t<a[j])//若双亲关键字小于,需要调整
         {
             a[i] = a[j];//将a[i]调整到双亲节点位置上
             i = j;//修改i和j的值,以便继续向下筛选
             j = 2*i;
         }
         else
         {
             break;//若双亲关键字大于两孩子关键字,筛选结束
         }
     }

     a[i] = t;//被筛选节点的值放入最终位置
}

void Heap_Sort(int a[],int n)
{
    int i,t;
    for (i=n/2; i>=0; i--)//循环建立初始堆
    {
       Sift(a,i,n-1);
    }

    for (i=n-1; i>=1; i--)//进行n-1次循环,完成堆排序
    {
        t = a[0];//将第一个元素同当前区间内的a[0]对换
        a[0] = a[i];
        a[i] = t;
        Sift(a,0,i-1);//筛选a[0]节点,得到i-1个节点的堆
    }
}

int main()
{
    int a[MaxSize];
    int i,n;
    cin>>n;
    for (i=0; i<n; i++)
    {
        cin>>a[i];
    }

    Heap_Sort(a,n);

    for (i=0; i<n; i++)
    {
        cout<<a[i]<<" ";
    }
    return 0;
}

4.运行截图:

5.算法分析:

          堆排序的时间效率与待排序数据序列的顺序无关。时间复杂度为Nlog2N,其产生的有序区一定是全局有序的,也就是说有序区中的所有元素的关键字一定大于或小于无序区中的所有元素的关键字,这样每一趟排序都会将一个元素放到最终的位置上。

                    

猜你喜欢

转载自blog.csdn.net/hw_xqzp_success/article/details/86244429