數據結構之“堆”


   對於堆的數據結構的介紹,在網上搜了下,具體講的不是很多。發現比較好的一篇介紹堆的博客是http://dongxicheng.org/structure/heap/。在此感謝他。

    通過對上面那篇博客的學習,然后自己也去翻了下《算法導論》里面關於堆排序(heapsort)的介紹。這樣就對堆有了更加深刻的認識,在此,我結合自己的一點點理解,主要還是基於上面那篇博客的內容(主要也是《算法導論》里的內容),也把他里面程序的一些錯誤改正,我把這篇博客寫了出來,以加深對堆的認識,並供自己日后溫習。后面我還要練習poj上幾天關於堆的題目,鏈接會在后面給出。

1.堆

    堆數據結構是一種數組對象,它可以被視為一科完全二叉樹結構。它的特點是父節點的值大於(小於)兩個子節點的值(分別稱為大頂堆和小頂堆)。它常用於管理算法執行過程中的信息,應用場景包括堆排序優先隊列等。

2. 堆的基本操作

    堆是一棵完全二叉樹,高度為O(lg n),其基本操作至多與樹的高度成正比。在介紹堆的基本操作之前,先介紹幾個基本術語:

 

    A:用於表示堆的數組,下標從1開始,一直到n

 

  PARENT(t):節點t的父節點,即floor(t/2)

 

  RIGHT(t):節點t的左孩子節點,即:2*t

 

    LEFT(t):節點t的右孩子節點,即:2*t+1

 

    HEAP_SIZE(A):堆A當前的元素數目

 3.保持堆的性質 Heapify(A,n,t)

 

     該操作主要用於維持堆的基本性質。假定以RIGHT(t)和LEFT(t)為根的子樹都已經是堆,然后調整以t為根的子樹,使之成為堆。

 

    

void Heapify(int A[],int i)
{
    int l=LEFT(i);
    int r=RIGHT(i);
    int largest;
    if(l<=HEAP_SIZE(A)) largest=A[l]>A[i]?l:i;
        if(r<=HEAP_SIZE(A)) largest=A[r]>A[largest]?r:largest;  //從i,2*i,2*i+1中找出最大的一個
        if(largest!=i)    //i不是最大的
    {
        swap(A[i],A[largest]);
        Heapify(A,largest);  //交換后,子樹有可能違反最大堆性質
    }
}

 


4.建堆 BuildHeap(A,n)

    操作主要是將數組A轉化成一個大頂堆。思想是,先找到堆的最后一個非葉子節點(即為第n/2個節點),然后從該節點開始,從后往前逐個調整每個子樹,使之稱為堆,最終整個數組便是一個堆。子數組A[(n/2)+1..n]中的元素都是樹中的葉子,因此都可以看作是只含有一個元素的堆。具體的過程我覺得看《算法導論》里面的圖的話理解應該很簡單,我找不到那圖。

void BuildHeap(int A[],)  
 {    
            int i;
            for(i = HEAP_SIZE(A)/2; i>=1; i--)    
               Heapify(A, i); 
  } 

 

5.堆排序算法   

     先用BuildHeapo將數組A[1..n]構造成一個最大堆。因為數組中最大元素在根A[1],則可以通過把它與A[n]交換來達到最終正確的位置。

 

void HeapSort(int A[])
{
    BuildHeap(A);
    for(i=HEAP_SIZE(A),i>1; i--)
    {
        swap(A[1],A[i]);
        HEAP_SIZE(A)=HEAP_SIZE(A)-1;
        Heapify(A,1);  //交換后新的根元素可能委培了最大堆的性質
    }
}

 

6.優先隊列  priority queue

    優先隊列是一種用來維護由一組元素構成的集合S的數據機構。相信大家對它都有所了解。雖然說c++里面有了priority_queue,但我們還是要了解它的一些基本構成及實現的代碼。

GETMAX:

該操作主要是獲取堆中最大的元素,同時保持堆的基本性質。堆的最大元素即為第一個元素,將其保存下來,同時將最后一個元素放到A[1]位置,之后從上往下調整A,使之成為一個堆。

void GetMaximum(int A[])   {
     int max = A[1];
     A[1] = A[n];
     HEAP_SIZE--;
     Heapify(A, n, 1);
     return max;
   } 

INSERT:

向堆中添加一個元素t,同時保持堆的性質。算法思想是,將t放到A的最后,然后從該元素開始,自下向上調整,直至A成為一個大頂堆。

void Insert(int A[], int i)   {  //i為插入的值
    int n=++HEAP_SIZE(A);     
    A[n] = -99999;//小無窮
     int p = n;
     while(p >1 && A[PARENT(p)] < i)  {
       A[p] = A[PARENT(p)];
       p = PARENT(p);
      }
      A[p]=i;
}

    總結。

     堆的最常見應用是堆排序,時間復雜度為O(N lg N)。如果是從小到大排序,用小頂堆;從大到小排序,用大頂堆。雖然堆排序是一個很漂亮的算法,但實際中,快排的一個好的實現往往優於堆排序。盡管這樣,對數據結構還是有着很大的用處,比如說優先隊列。

    例子:  在O(n lg k)時間內,將k個排序表合並成一個排序表,n為所有有序表中元素個數。

【解析】取前100 萬個整數,構造成了一棵數組方式存儲的具有小頂堆,然后接着依次取下一個整數,如果它大於最小元素亦即堆頂元素,則將其賦予堆頂元素,然后用Heapify調整整個堆,如此下去,則最后留在堆中的100萬個整數即為所求 100萬個數字。該方法可大大節約內存。

   例子:一個文件中包含了1億個隨機整數,如何快速的找到最大(小)的100萬個數字?(時間復雜度:O(n lg k))

堆是一種非常基礎但很實用的數據結構,很多復雜算法或者數據結構的基礎就是堆,因而,了解和掌握堆這種數據結構顯得尤為重要。

 


免責聲明!

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



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