1. 程式人生 > >堆的構建, 以及堆排序的c++實現

堆的構建, 以及堆排序的c++實現

    堆是一種資料結構,就是每個節點根據某種規則排序, 從根節點往下都符合某種規律,

根節點的值比所有節點的值都大, 稱為最大堆;

根節點的值比所有節點的值都小, 稱為最小堆;


堆排序 step:

        一)建樹部分

                    1.   找到一個樹的最後一個非葉節點, 計算公式為 (n-1)  / 2, 然後遍歷樹的每個非葉節點,使其符合堆的規則

                                                

紅圈為最後一個 非葉 子節點

 void make_heap(int *a, int len)
 {
    for(int i =  (len-1)/2; i >= 0; --i)    //遍歷每個 非葉子節點
adjust_heap(a, i, len);//不用考慮那麼多, 用面向物件的思鄉去考慮, }                                    //這個函式的作用就是用來使 當前節點的子樹 符合 堆的規律

                    2. 要使某節點的當前節點的字數符合 堆規律,需要以下操作:

void adjust_heap(int* a, int node, int size)
{
        int left = 2*node + 1;
        int right = 2*node + 2;  
        int max = node;
        if( left < size && a[left] > a[max])    
                max = left;
        if( right < size && a[right] > a[max])
                max = right;
        if(max != node)
        {   
                swap( a[max], a[node]);    //交換節點
                adjust_heap(a, max, size);     很關於這裡遞迴的解釋請看下面
        }                      }


90 和 7交換後, 發現 7 不能管住 原來90的兩個兒子, 那怎麼辦呢??那就繼續呼叫adjust() 讓他符合

所以需要遞迴。


到此為止

這個整個樹都符合 堆的規律了, 最大堆就已經建造好了。

二)排序部分

                    

現在我們需要把最大堆中的元素排降序, 該如何呢  ???            

將堆的頂部,與最後一個元素交換。 此時除了最後一個元素外, 剩下元素所組成的樹已經不是 堆了。(因為此時頂部的元素可能比較小)。    所以, 要將剩下的元素通過 adjust函式調整成 堆。

    然後繼續將剩餘元素中的最後一個元素 與 新堆的頂部交換 。。。。。。

程式碼如下:

 for(int i = len - 1; i >= 0; i--)
 {
         swap(a[0], a[i]);           // 將當前最大的放置到陣列末尾
         adjust_heap(a, 0 , i);              // 將未完成排序的部分繼續進行堆排序
 }

完整程式碼如下:

#include <iostream>
using namespace std;

void adjust_heap(int* a, int node, int size)
{
        int left = 2*node + 1;
        int right = 2*node + 2;
        int max = node;
        if( left < size && a[left] > a[max])
                max = left;
        if( right < size && a[right] > a[max])
                max = right;
        if(max != node)
        {
                swap( a[max], a[node]);
                adjust_heap(a, max, size);
        }
}

void heap_sort(int* a, int len)
{
        for(int i = len/2 -1; i >= 0; --i)
                adjust_heap(a, i, len);

        for(int i = len - 1; i >= 0; i--)
        {
                swap(a[0], a[i]);           // 將當前最大的放置到陣列末尾
                adjust_heap(a, 0 , i);              // 將未完成排序的部分繼續進行堆排序
        }
}

int main()
{

        int a[10] = {3, 2, 7, 4, 2, -999, -21, 99, 0, 9  };
        int len= sizeof(a) / sizeof(int);

        for(int i = 0; i < len; ++i)
                cout << a[i] << ' ';
        cout << endl;

        heap_sort(a, len);

        for(int i = 0; i < len; ++i)
                cout << a[i] << ' ';
        cout << endl;

        return 0;
}

            然而堆排序中的幾個函式,還是可以通過優化來提升 時間複雜度的。比如 adjust()函式的優化

如果在用 adjust在調整最大堆時, 交換需要以下操作:

            temp  = a[node];

            a[node] = a[max];

            a[max] = temp;

需要操作三次,100000次 的交換需要操作 300000次。太過於耗時。所以我們可以做類似於插入排序的優化。

        if( 節點的某一個兒子 > 節點本身 )

                  用 temp 把兒子存起來,  並把節點賦值給兒子;

    用temp上浮一層到節點的位置, 繼續執行判斷, 這樣一層層不停的上浮。直到不符合條件

        if( temp < 同一層的兄弟節點 || temp < 父親節點)

                   把 temp 賦值給當前層的元素;

       這樣省去了每次的交換, 100000的操作只需要操作100000次。大大提高了效率

本人才疏學淺,孤陋寡聞(略去一萬字),有錯誤請指出。