Advertisement

【算法导论】第六章 堆 学习笔记

阅读量:

二叉堆是一个 ,可以看做一个近似完全二叉树,对于一个长度为n的数组给定一个数组下标i,~i=0,1,2,…,n-1,则:

最大堆

除根节点外,所有节点满足:

最小堆

除根节点外,所有节点满足:

堆的高度

定义:叶节点到根的最短路径上边的长度为堆的高。

定理6.1

对于节点数为的堆,其所有节点的可能取值高度(即,以节点i根节点 的子树的高)为:h=0,1,…lgn取下界,且高度为h的节点,至多有{n}/{n^{h+1}}取上界个。

堆排序

以最大堆为例:

排序的整个过程可以看做如下:

复制代码
 建堆 BUILD_MAX_HEAP
    2. 堆维护 MAX_HEAPITY
    3. 断尾重构 HEAPSORT
    
      
      
      
    
    AI写代码

堆维护 MAX_HEAPITY

MAX_HEAPITY堆维护是建堆过程中至关重要的一步,它负责维护最大堆的性质不变,即根节点一定大于等于子节点。

代码思想(伪代码)
复制代码
    MAX_HEAPITY(A,i):
    left=i*2+1;
    right=i*2+2;
    //假设以left和right均为最大堆
    //判断left和right的合理性 < heap_size
    //比较节点与left、right的大小,
    //将大的节点与i交换,并递归调用MAX_HEAPITY(A,max_index)
    
      
      
      
      
      
      
      
    
    AI写代码
复杂度分析

从上可知,对于节点至多交换h_i次,即被从堆顶被交换到堆底。所以时间复杂度为O(h)=O(lgn)

也可用递推公式T(n)\le T(2n/3)+\Theta(1),通过主定理求解得T(n)=O(lgn)

建堆 BUILD_MAX_HEAP

建堆主要是将无序数组按照树顺序,建立成最大(小)堆的过程。

已知对于节点数为n,~i=0,1,2…n-1的二叉树,其叶子节点的序号一定从(n+1)/2开始,则在建堆初始化时,可以把第(n+1)/2,(n+1)/2+1,…,n-1个叶子节点看做单节点最大堆。故建堆循环应从后往前,直到根节点i=0

代码思想(伪代码)
复制代码
    BUILD_MAX_HEAP(A):
    for i=(n+1)/2-1 to 0:
        MAX_HEAPITY(A,i)
    
      
      
      
    
    AI写代码
复杂度分析

结论:O(n)的线性复杂度。

分析:由伪代码可知,时间复杂度的上界为(n+1)/2*O(MAX\_HEAPITY),由于MAX_HEAPITY的复杂度与节点i所处的高度有关,我们可以利用定理6.1得到一个更紧确的界:即把要经过MAX_HEAPIFY的节点复杂度按其高度进行复杂度求和。通过变换和得线性复杂度。详见算法导论p88。

堆排序算法

用断尾重构的方法:每次把堆顶元素放至数组尾部,相应的堆size减小1

代码思想(伪代码)
复制代码
    HEAPSORT(A):
    for i=A.length to 1:
        exchange A[0],A[i]
        --A.heap_size;
        MAX_HEAPIFY(A,0);//对新的堆顶进行排序维护
    
      
      
      
      
      
    
    AI写代码
复杂度分析

结论:O(nlgn)

完整代码

复制代码
    //MaxHeap.h
    #ifndef LINTCODE_MAXHEAP_H
    #define LINTCODE_MAXHEAP_H
    
    #include <vector>
    
    using namespace std;
    
    //原地排序算法
    class MaxHeap {
    private:
    void exchangeNum(vector<int> &A, int i, int j);
    
    void maxHeapity(vector<int> &A, int index, int &heap_size);
    
    void buildMaxHeap(vector<int>& A, int &heap_size);
    
    public:
    void heapSort(vector<int>& A);
    };
    
    
    #endif //LINTCODE_MAXHEAP_H
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码
复制代码
    //MaxHeap.cpp
    
    #include "MaxHeap.h"
    using namespace std;
    
    void MaxHeap::exchangeNum(vector<int> &A, int i, int j) {
      int temp = A[i];
      A[i] = A[j];
      A[j] = temp;
    }
    
    //数组下标从0开始
    void MaxHeap::maxHeapity(vector<int> &A, int index, int &heap_size) {
      int left = index * 2 + 1;
      int right = index * 2 + 2;
      int i = index;
      i = (left < heap_size && A[left] > A[i]) ? left : i;
      i = (right < heap_size && A[right] > A[i]) ? right : i;
    
      if (i != index) {
    exchangeNum(A, i, index);
    //递归调用
    maxHeapity(A, i, heap_size);
      }
    
    }
    
    void MaxHeap::buildMaxHeap(vector<int> &A, int &heap_size) {
      int n = A.size();
      int leaf_num = (n + 1) / 2; //节点数为n的堆,的叶子节点号为(n+1)/2,(n+1)/2+1,...,n
      for (int i = leaf_num - 1; i >= 0; --i) {
    maxHeapity(A, i, heap_size);
      }
    }
    
    //断尾法,得排序数组
    void MaxHeap::heapSort(vector<int> &A) {
      int heap_size = A.size();
      buildMaxHeap(A, heap_size);
    
      for (int i = A.size() - 1; i > 0; --i) {
    exchangeNum(A, 0, i); //交换尾部和当前堆顶元素
    --heap_size;
    maxHeapity(A, 0, heap_size);
      }
    }
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

优先队列

我们常用最大堆和最小堆来维护一个优先队列。

总结

  1. 堆排序是基于比较的排序,性能稳定,排序不稳定。
  2. 性能稳定:堆排序的(最好/平均/最差)时间复杂度均为。
  3. 只需要O(1)的辅助空间,既最高效率又节省空间
  4. 缺点:堆维护比较频繁,对于数组元素经常变动的情况下,不适合采用堆排序

全部评论 (0)

还没有任何评论哟~