zoukankan      html  css  js  c++  java
  • 堆排序算法的具体分析和实现

    定义

    堆就是完全二叉树的数据结构,堆排序是利用二叉树的孩子与双亲节点的比较来实现的排序方法。

    大顶堆:每个节点的值都大于或者等于它的左右子节点的值。

    小顶堆:每个节点的值都小于或者等于它的左右子节点的值。

    这里使用的是大顶堆。

    基本思想

    堆排序的基本思想是:
    1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
    2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
    3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。

    代码

    
    /**
     * 总结
     * 堆排序就是通过一个完全二叉树, 把数据按照1开始的顺序编排
     * 然后, 进行一个算法, 找出初始堆, 发现一个确定的最大值, 并抹除输出
     * 接着在进行该算法, 找出第二个最大的数字
     * 
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #define MAX 200
    typedef int KeyType;
    typedef int InfoType;
    typedef struct SS
    {
        KeyType key;   //关键字项
        InfoType data; //其他数据项
    } RecType;         //排序的元素的类型
    
    //交换函数
    void swap(RecType &a, RecType &b)
    {
        RecType c;
        c = a;
        a = b;
        b = c;
    }
    
    RecType *Init(int* a, int n)
    //就是将数组存入对应的关键字的结构体中
    {
        int i;
        RecType * R;
    
        R = (RecType *)malloc(sizeof(RecType)*MAX);
    //注意这是一个偷懒的办法,浪费了空间,虽然提高了速度
    //正常应该在创建的时候,即下面的for循环中一个个的开辟空间
        for (i = 1; i <= n; i++)
        {
            R[i].key = a[i-1];
        }
        return R;
    }
    
    void sift(RecType R[], int low, int high)
    //建立初始堆
    //就是从low开始的位置,把low看成根节点的,并且把比low大的孩子节点
    //和low替换,实现效果就是low节点和其子树都满足大根堆的条件。
    {
        int i = low, j = 2 * i;
        RecType temp = R[i];
        while (j <= high)
        {
            if (j < high && R[j].key < R[j + 1].key)//j指向左右子树中大的那个,为了下面的替换
                j++;
            
            if(temp.key < R[j].key)  //如果根节点比子树小,就替换
            {
                R[i] = R[j];
                i = j;	//i指向j的左孩子,因为可能根节点实在太low特别小,
               			// 可能会比孙子还小,所以要找找有没有大的孙子
                j = 2*i;
            }
            else
            	 break;  //如果根节点都比孩子大,那么它的位置保住了,退出
        }
    
        R[i] = temp; //这一句是为了照顾第二个if情况的,如果比孙子辈小的话
        			//那前面已经根节点替换成了孙子, 而孙子的位置需要它来顶替
        			//当然如果没有比较小的话,这一句话也没有影响,R[i]本来就是temp
    }
    
    void HeapSort(RecType R[], int n)
    {
        int i;
    
        for(i = n/2; i >= 1; i--)
            sift(R, i, n);       //这个地方只要一半就好,因为子树作为2*(n/2) == n
            					//就是建立初始堆,此时并没有完成排序
            					//但是初始堆代表着树的根节点是最大值
    
        for (i = n; i >= 2; i--){    //接下来就是去掉最大值,每次跑一遍
            swap(R[1], R[i]);     //最大值被最后一个值交换掉,并且不再进行运算
            sift(R, 1, i - 1);    //接着继续通过刚才的函数找最大值
        }
    }
    
    int main ()
    {
        int a[10] = {6, 32, 12, 34, 25, 76, 34, 1, 9, 98};
        RecType *R;
        R = Init(a, 10);
    
        HeapSort(R, 10);
    
        int  i;
        for(i = 1; i <= 10; i++)
        {
            printf("%d ", R[i].key);
        }
        system("pause");
        return 0;
    }
    

    复杂度分析

    因为堆排序无关乎初始序列是否已经排序已经排序的状态,始终有两部分过程,构建初始的大顶堆的过程时间复杂度为O(n),交换及重建大顶堆的过程中,需要交换n-1次,重建大顶堆的过程根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为nlogn。所以它最好和最坏的情况时间复杂度都是O(nlogn),空间复杂度O(1)。

    Luky

    点赞哦

  • 相关阅读:
    CSS 盒子模型
    一个好的创业型团队需要下面几种人
    解决关闭Hadoop时no namenode to stop异常
    Hadoop RPC实例和通信过程分析
    实现RPC框架,几行代码就够了
    Linux 文件权限详解
    HDFS Java API 详解
    HDFS客户端的权限错误:Permission denied
    MongoDB GroupBy操作, 结果集大小限制问题。
    MongoDB 查询超时异常 SocketTimeoutException
  • 原文地址:https://www.cnblogs.com/billyme/p/13441797.html
Copyright © 2011-2022 走看看