zoukankan      html  css  js  c++  java
  • (算法)堆与堆排序

    主要内容:

    1、什么是堆?

    2、如何建堆

    3、堆排序

    4、参考代码

    一、什么是堆?

    “堆”是个很有趣的数据结构,是个完全二叉树。

    “堆”的特性:每个节点的键值一定总是大于(或小于)它的父节点(大于:称为“最大堆”,小于:称为“最小堆”),或者说每个节点总是大于或小于它的子节点。

    对于最大堆而言,根节点为最大值;对于最小堆而言,根节点为最小值。

    通常“堆”是通过数组来实现的,这样可以利用数组的特点快速定位指定索引的元素。

    因此,当我们得知某个节点的索引为 i 时,可以通过以下“堆”的定义很容易地定位到它的子节点和父节点。

    在起始索引为 0 的“堆”中: 
    1) 堆的根节点将存放在位置 0 
    2) 节点 i 的左子节点在位置 2 * i + 1 
    3) 节点 i 的右子节点在位置 2 * i + 2 
    4) 节点 i 的父节点在位置 floor( (i - 1) / 2 )   : 注 floor 表示“取整”操作 

    在起始索引为 1 的“堆”中: 
    1) 堆的根节点将存放在位置 1 
    2) 节点 i 的左子节点在位置 2 * i 
    3) 节点 i 的右子节点在位置 2 * i + 1 
    4) 节点 i 的父节点在位置 floor( i / 2 )           : 注 floor 表示“取整”操作 

    二、如何建堆?

    根据上述对于堆的定义,建立一个堆,首先需要构建一棵完全二叉树,树的节点需要满足:每个节点的值都不大于或不小于其父节点的值,即A[PARENT[i]] >=/<= A[i]。

    给定一个无序数组,如何构建一个最大堆呢?

    首先,将该无序数组构建为一棵完全二叉树,然后根据每个节点需要满足的条件,进行二叉树的调整。

    建立一个最大堆,而建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。

    调堆的过程应该从最后一个非叶子节点(最后一个节点的父节点)开始,假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。

    那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始,分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中是A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1.1转到图1.2。

    image

    所以建堆的过程就是

       for ( i = headLen/2-1; i >= 0; i++)
    ​      do AdjustHeap(A, heapLen, i)

    调堆:

    如果初始数组是非降序排序,那么就不需要调堆,直接就满足堆的定义,此为最好情况,运行时间为Θ(1);

    如果初始数组是如图1.5,只有A[0] = 1不满足堆的定义,经过与子节点的比较调整到图1.6,但是图1.6仍然不满足堆的定义,所以要递归调整,一直到满足堆的定义或者到堆底为止。

    如果递归调堆到堆底才结束,那么是最坏情况,运行时间为O(h) (h为需要调整的节点的高度,堆底高度为0,堆顶高度为floor(logn) )。

    调堆的时间复杂度:O(logn)

    建堆的时间复杂度:O(n)

    关于时间复杂度的分析:参考http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html

    三、如何进行堆排序?

    根据“堆”的特性,我们可以从无序序列的最后一个节点的父节点开始,对其进行“下移”操作,直到序列的第一个节点为止。这样就可以保证每个节点都在合适的位置上,也就建立起了一个“堆”。 
    但是“堆”并不是一个完全排序的序列,因为“堆”只保证了父节点与子节点的位置关系,但并不保证左右子节点的位置关系。那么我们如何进行“堆排序”呢? 

    由于一个“堆”的根节点必然是整个序列中最大的元素,因此对于一个排序的序列而言,每个“堆”中我们只能得到一个有效的元素。如果我们拿掉根节点,再对剩下的序列重新排列组成一个“堆”,反复如此,我们就可以依次得到一个完整的排序序列了。 
    当然,为了简化操作,每次我们只需要把根节点与最后一个位置的节点交换,然后把最后一个位置排除之外,对根节点进行“下移”操作即可。

    堆排序的时间复杂度:O(nlogn)

    关于时间复杂度的分析:参考http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html

    建堆完成之后,堆如图1.7是个大根堆。

    将A[0] = 8 与 A[heapLen-1]交换,然后heapLen减一,如图2.1,然后AdjustHeap(A, heapLen-1, 0),如图2.2。如此交换堆的第一个元素和堆的最后一个元素,然后堆的大小heapLen减一,对堆的大小为heapLen的堆进行调堆,如此循环,直到heapLen == 1时停止,最后得出结果如图3。

    image

    image

    四、代码

    #include <iostream>
    
    using namespace std;
    
    int leftChild(int i){
        return 2*i+1;
    }
    
    int rightChild(int i){
        return 2*i+2;
    }
    
    // adjust heap
    void makeMaxheap(int *A,int len,int i){
        int left=leftChild(i);
        int right=rightChild(i);
        int largest=i;
        int tmp;
    
        while(left<len || right<len){
            if(left<len && A[largest]<A[left])
                largest=left;
            if(right<len && A[largest]<A[right])
                largest=right;
            if(largest!=i){
                tmp=A[largest];
                A[largest]=A[i];
                A[i]=tmp;
    
                i=largest;
                left=leftChild(i);
                right=rightChild(i);
            }
            else
                break;
        }
    }
    
    void makeMinheap(int *A,int len,int i){
        int left=leftChild(i);
        int right=rightChild(i);
        int smallest=i;
        int tmp;
    
        while(left<len || right<len){
            if(left<len && A[smallest]>A[left])
                smallest=left;
            if(right<len && A[smallest]>A[right])
                smallest=right;
            if(smallest!=i){
                tmp=A[smallest];
                A[smallest]=A[i];
                A[i]=tmp;
    
                i=smallest;
                left=leftChild(i);
                right=rightChild(i);
            }
            else
                break;
        }
    }
    
    // build heap
    void buildMaxHeap(int* A,int len){
        int begin=len/2-1;
        for(int i=begin;i>=0;i--)
            makeMaxheap(A,len,i);
    }
    
    void buildMinHeap(int* A,int len){
        int begin=len/2-1;
        for(int i=begin;i>=0;i--)
            makeMinheap(A,len,i);
    }
    
    // heap sort
    void heapMaxSort(int* A,int len){
        int hlen=len;
        int tmp;
    
        buildMaxHeap(A,hlen);
    
        while(hlen>0){
            tmp=A[hlen-1];
            A[hlen-1]=A[0];
            A[0]=tmp;
            hlen--;
            makeMaxheap(A,hlen,0);
        }
    }
    
    void heapMinSort(int* A,int len){
        int hlen=len;
        int tmp;
    
        buildMinHeap(A,hlen);
    
        while(hlen>0){
            tmp=A[hlen-1];
            A[hlen-1]=A[0];
            A[0]=tmp;
            hlen--;
            makeMinheap(A,hlen,0);
        }
    }
    int main()
    {
        int A[]={1,3,4,5,7,2,6,8,0};
        int len=sizeof(A)/sizeof(A[0]);
    
        buildMaxHeap(A,len);
        for(int i=0;i<len;i++)
            cout<<A[i]<<" ";
        cout<<endl;
    
        int B[]={1,3,4,5,7,2,6,8,0};
        buildMinHeap(B,len);
        for(int i=0;i<len;i++)
            cout<<B[i]<<" ";
        cout<<endl;
    
        heapMaxSort(A,len);
        for(int i=0;i<len;i++)
            cout<<A[i]<<" ";
        cout<<endl;
    
        heapMinSort(B,len);
        for(int i=0;i<len;i++)
            cout<<B[i]<<" ";
        cout<<endl;
        return 0;
    }

    五、参考文章

    http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html

    http://wxg6203.iteye.com/blog/668968

  • 相关阅读:
    super返回不过来
    对象与内存控制
    java数组学习
    careercup-递归和动态规划 9.8
    careercup-递归和动态规划 9.7
    careercup-递归和动态规划 9.6
    careercup-递归和动态规划 9.5
    careercup-递归和动态规划 9.4
    careercup-递归和动态规划 9.3
    careercup-递归和动态规划 9.2
  • 原文地址:https://www.cnblogs.com/AndyJee/p/4612821.html
Copyright © 2011-2022 走看看