為什么要堆初始化-堆初始化時間復雜度?


結論:

  堆初始化的時間復雜度為 O(N)

  插入成堆的時間復雜度為 O(N Log N)

 

 

!!!閱讀前需先了解完全二叉樹,堆排序算法,不清楚移步

  完全二叉樹

  堆排序

 

堆排序偽代碼:

HEAPSORT( A )

  BUILD_MAX_HEAP(A);        //堆初始化,本文討論的主題

  for i=A.length down to 2       

    exchange A[1] with A[i]

    A.length=A.length-1

    MAX_HEAPIFY(A,1)       //自上而下維護堆

 

 

嘿!堆為什么要初始化?

  一日,在實現堆排序算法時,室友好奇的一問:你這個數組為什么要先初始化成堆呀?這么復雜,聲明一個空堆,不斷的把數組里的元素插入進去不就好了嗎?

欸!好像是這么一回事呢。那堆排序算法里的這個初始化是不是太多余了?

 

  首先,先說明維護堆的兩個操作,作為接下來分析的基礎:

typedef int* HEAP;         //int為元素的堆

 

    1.自上而下的維護一棵子樹為大根堆,在這里將該方法命名為:adjust_down(HEAP heap,int pos,int len)算法執行次數取決於該節點到葉子節點的距離。

//算法演示
void adjust_down(HEAP heap,int pos,int len)
{
    heap[0]=heap[pos];
    for(int i=pos<<1;i<=len;i<<=1){
        if(i<len&&heap[i+1]>heap[i])
            i++;
        if(heap[i]>heap[0])
            heap[pos]=heap[i],pos=i;
        else break;
    }
    heap[pos]=heap[0];
}

    2.自下而上的將一個元素插入到堆中合適的位置,在這里將該方法命名為:adjust_up(HEAP heap,int pos);算法執行次數取決於該節點到的距離。

//算法演示
void adjust_up(HEAP heap,int pos)
{
    heap[0]=heap[pos];
    int parent=pos/2;
    while(parent>0&&heap[parent]<heap[0]){
        heap[pos]=heap[parent];
        pos=parent,parent/=2;
    }
    heap[pos]=heap[0];    
}

 

 

室友說的對嗎?

  從描述上來看,首先空堆是滿足條件的,在每次插入前堆都是大根堆,那么插入以后執行adjust_up(),最后確實生成了一棵大根堆。

  而堆的初始化過程為:從元素的第len/2個元素開始到第一個元素,不斷的調用dajust_down(),最后也生成一個大根堆。

  兩個方法最后都生成了大根堆。

 

那為什么還要有堆初始化的過程呢?

  既然能實現同樣的功能,那效率上是否有差異呢?

  最直觀的,從空間上看,因為堆的實現方式是數組,如果先申請一個堆,在不斷的往里面插入,那么堆的空間與數組空間一樣大,需要相當於兩個數組的容量。

如果采用在數組上初始化,則不需要多余的空間。

  從時間上來看,堆初始化是自上而下,插入成堆是自下而上;

 

  堆初始化:因為每個節點需要的比較的次數取決該節點到葉子節點的距離(原因參考代碼:adjust_down() )。

    1)設樹的深度從0開始計數,樹的深度為K,,結點個數為N。

    2)深度為K-i的結點,需要的比較次數為 i。

    3)除最后一層節點可能不滿以外,深度為K-i的那一層結點總數為$2^{K-i}$

  故總的比較次數 $S=\sum_{i=1}^{K} {2^{K-i}*i}$

  則$2*S=\sum_{i=1}^{K} {2^{K-i+1}*i}$

  得$S=2*S-S=\sum_{i=1}^{K} {2^{K}}+K  = 2^{K+1}-2+K$

  因為$\sum_{i=1}^{K} {2^{K}} \rightarrow N ,$

  故初始化的時間復雜度為 O(N),

 

  插入成堆:因為每個結點需要的比較次數取決於該結點到根的距離(原因參考代碼:adjust_up() )。

    1)分析時,以一顆滿二叉樹為代表,以簡化分析過程,其他相關參數如上定義。

    2)深度為i的結點,需要的比較次數為 i。

    3)深度為i的那一層結點總數為$2^{i}$

  故總的比較次數 $S=\sum_{i=1}^{K} {2^{i}*i}$

  則$2*S=\sum_{i=1}^{K} {2^{i+1}*i}$

  得$S=2*S-S=(K-1)*2^{K+1}+2^K-2$

  因為$\sum_{i=1}^{K} {2^{K}} \rightarrow N ,$

  故插入成堆的時間復雜度為O(N Log N)。

 

 

綜上所述,堆初始化在空間上,時間上均更有優勢,所以是有必要的。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM