zoukankan      html  css  js  c++  java
  • 堆排序

    堆排序


    参考资料

    算法导论

    2 数据结构(C语言版)


    堆的概述

    二叉 堆数据结构是一种数组对象,它可以被视为一颗完全二叉树。树中每个节点与数组中存放该节点值的那个元素对应。数的每一层都是填满的,最后一层可能除外。表示堆的数组A是一个具有两个属性的对象:length [ A ] 是数组中的元素个数,heap-size [A] 是存放在中的堆的元素个数。

    根据完全二叉树的性质,我们可以总结出如下性质:数的根为A [ 1 ] ,对于某个结点的下标 ,其父节点为 Parent[i] = i/2 ,左孩子 Left[ i ] = 2*i ,右孩子 Right[ i ] = 2*i + 1,该部分的结论是显而易见的。可参考二叉树章节)

    在具体的代码实现中,左孩子2*i 可通过左移1位来实现,即i << 1;同理,右孩子为左移1位加1或者与1作或运算,即 i << 1 + 1 或 i << 1|1 ;父节点为 i >> 1 。算法导论中建议将这些计算过程通过“宏”或“内联函数”实现。

    二叉堆有两种:大堆 大根堆 和小堆 小根堆 )。大堆与小堆内部的节点需要满足堆的特点。对于大堆来说,节点i (非根节点的值 <= i 的父节点的值,根节点是最大的值。

    对于小堆来说,节点i ( 非根节点的值 <= i 的左右孩子节点的值,根节点的值时最小的。

    堆可以被看成是一棵树,而且是一颗完全二叉树,假设该堆有 个节点,那么该堆的高度为 lg n 

    建小堆过程

    现有8个元素的无序序列A = { 49,38,65,97,76,13,27,49 },将该序列建立为一个完全二叉树A。如下图 所示


    我们从第 4 (完全二叉树共8个节点,第8个节点的父节点为 ,所以从4处开始调整 个节点开始调整。此时 A[4] > A[8],于是将两者位置对换。如下图B所示


    此时从第3个节点 节点65 ) 开始调整,65 和它的两个孩子比较,发现13较小,于是将13 与 65 对换。如下图 所示。


    3个节点比较完之后,我们往回退,从第2个节点开始调整。此时第2个节点比孩子节点都小,故不作调整。

    于是又回退,从第1个节点开始进行调整。第1个节点与两个孩子比较,发现右孩子较小,于是将13与 49 进行对换。如下图 所示。


    对换之后,49又比它的孩子节点27 大,于是又需要调整。如下图E所示。至此整个调整完成,图E即所要求的小堆的特性。


    建小根堆代码

    //调整小根堆,从节点i 开始调整   
    //存放到数组中的节点是从1开始的
    void min_heapify(int *a, int i , int len)
    {
        int smaller = i;
        int l = i << 1;
        int r = i <<1 | 1;
        if( l <= len && a[l] < a[smaller])
            smaller = l ;
        if( r <= len && a[r] < a[smaller] )
            smaller = r ;
        if( smaller != i)  //i节点比孩子节点中至少1个要大,此时需要调整
        {
            int tmp = a[i];
            a[i] = a[smaller];
            a[smaller] = tmp;         
            min_heapify(a,smaller,len);   //为什么从smaller位置开始调整? 因为此处的值进行了与其父节点互换
    
                                         //所以这个位置可能破坏了堆的特性,需要重新调整,而smaller的兄弟节点没有动,故其堆特性没有发生变化,不需要调整
        }
    }
    //建立小根堆
    void build_min_heap(int *a , int len)
    {
        for( int i = len/2 ; i >=1; i--)
            min_heapify( a , i,len );
    }
    

    堆排序算法

    堆排序我们以小根堆来进行排序。我们知道小根堆中的非叶子节点总是比它的孩子节点要小。当我们建立了一个小根堆之后,只需要取出根节点,该根节点也就是待排序序列的第一个元素了。此时将根节点与小根堆中最后一个元素A[n]互换,此时A[n]元素成了根节点,必然破换小根堆的特性,于是需要对小根堆进行调整了。调整完之后,新的小根堆的根节点就是新小根堆的最小元素了,于是又与小根堆的A[n-1]互换,以此循环下去,直到只剩2个节点。


    堆排序算法的伪码如下

    HEAPSORT ( A)
    BUILD-MIN-HEAP(A)
    for i ← length [ A ]  downto 2
    do  exchange  A [ 1 ] -- A [ i ]
        heap-size [ A ] ← heap-size [ A ]-1
        MIN-HEAPIFY ( A )

    堆排序代码

    //堆排序
    void heap_sort(int *a , int len)
    {
        build_min_heap(a, len);
        for(int i = len ; i >= 2; i--)
        {
            cout << a[1] << ' ';
            a[1] = a[i];
            min_heapify(a,1,i-1);
        }
        cout << a[1] << endl;
    }
    


    演示代码

    PS:只要将上述代码合并到一起并添加相应的头文件即可运行

    int main()
    {
        int a[9] = {0,49,38,65,97,76,13,27,49};
        build_min_heap(a,8);
        cout << "建堆 :" << endl;
        for(int i = 1; i <=8; i++)
            cout << a[i] << ' ';
    
        cout << endl;
        cout << "堆排序 :" << endl;
        heap_sort(a,8);
        cout << endl;
        return 0;
    }



  • 相关阅读:
    一些常用的Unix命令
    Shortcut Blocks with Symbol to_proc 通过to_proc为代码块逻辑命名
    Using with_scope 使用with_scope
    Move Find into Model 将查询方法从控制器上移至模型
    Find Through Association 使用级联查询
    Dynamic find_by Methods 使用动态的find_by方法
    Caching with Instance Variables 缓存与实例变量
    深入浅出object-c
    IT大神进化史-第二章
    IT大神进化史-第一章
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3225991.html
Copyright © 2011-2022 走看看