• 堆排序-C语言实现


    堆排序

           堆排序是利用堆的性质进行的一种选择排序。下面先讨论一下堆。

    1.堆

      堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:

      Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]

      即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。

      堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。

    2.堆排序的思想

       利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。

        其基本思想为(大顶堆):

        1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无须区;

        2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n]; 

        3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

        操作过程如下:

         1)初始化堆:将R[1..n]构造为堆;

         2)将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。

        因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。

        下面举例说明:

         给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。

        首先根据该数组元素构建一个完全二叉树,得到

     
     然后需要构造初始堆,则从最后一个非叶节点开始调整,调整过程如下:

    20和16交换后导致16不满足堆的性质,因此需重新调整

    这样就得到了初始堆。

    即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。有了初始堆之后就可以进行排序了。

    此时3位于堆顶不满堆的性质,则需调整继续调整

     这样整个区间便已经有序了。
        从上述过程可知,堆排序其实也是一种选择排序,是一种树形选择排序。只不过直接选择排序中,为了从R[1...n]中选择最大记录,需比较n-1次,然后从R[1...n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。对于n个关键字序列,最坏情况下每个节点需比较log2(n)次,因此其最坏情况下时间复杂度为nlogn。堆排序为不稳定排序,不适合记录较少的排序。
     
    堆排序c语言算法:
    #include<stdio.h>
    typedef int ElementType;
    int arr1[11]={0,2,87,39,49,34,62,53,6,44,98};
    #define LeftChild(i) (2 * (i) + 1)
    
    void Swap(int* a,int* b)
    {
        int temp=*a;
        *a=*b;
        *b=temp;
    }
    
    
    void PercDown(int  A[], int i, int N)
    {
        int child;
        ElementType Tmp;
    
        for (Tmp = A[i]; 2*i+1 < N; i = child){
            child = 2*i+1; //注意数组下标是从0开始的,所以左孩子的求发不是2*i
            if (child != N - 1 && A[child + 1] > A[child])
                ++child;                //找到最大的儿子节点
            if (Tmp < A[child])
                A[i] = A[child];
            else
                break;
        }
        A[i] = Tmp;
    }
    
    void HeapSort(int A[], int N)
    {
        int i;
        for (i = N / 2; i >= 0; --i)
            PercDown(A, i, N);    //构造堆
        for(i=N-1;i>0;--i)
        {
            Swap(&A[0],&A[i]);        //将最大元素(根)与数组末尾元素交换,从而删除最大元素,重新构造堆
            PercDown(A, 0, i);
        }
    }
    
    void Print(int A[],int N)
    {
        int i;
        for(i=0;i<N;i++)
        {
            printf(" %d ",A[i]);
        }
    }
    int main()
    {
        int arr[10]={2,87,39,49,34,62,53,6,44,98};
        Print(arr,10);
        printf("
    ");
        HeapSort(arr,10);
        Print(arr,10);
        printf("
    ");
        return 0;
    }

    运行结果:

  • 相关阅读:
    Linux之C编译器gcc和makefile使用简介
    基于OWin的Web服务器Katana发布版本3
    OAuth和OpenID的区别
    关于 Token,你应该知道的十件事
    HTTP Header 详解
    Entity Framework教程及文章传送门
    CSP(Content Security Policy) 入门教程
    gulp构建工具的几个使用技巧
    浅谈程序员的英语学习
    如何选择正确的angular2学习曲线?
  • 原文地址:https://www.cnblogs.com/wuchanming/p/3821607.html
Copyright © 2020-2023  润新知