算法設計和數據結構學習_1(一道堆排序作業題)


 

  前言

  這時上次學妹課程的一道作業題,我花了點時間做了下,其題目內容為:

    試寫一程序,可以對一二元樹(binary)進行堆積排序(heap sort)

  (a)使用者可自己決定輸入二元樹的節點個數

         (i)node數不超過50       

  (b)節點值由隨機方式產生,並印出隨機設值結果

         (i)以時間復雜度O(n)的方式設值 (ii)假設值不可重復

         (iii)最大值不可大於node數 (例如node數為9,因此最大值為9) 

  (c)使用者可決定使用MAX-HEAP或者是MIN-HEAP來排序

  (d)須將重建堆積得過程印出,以及最后輸出排序結果

  Sample Output:

  

 

  后面在網上查了下堆排序的介紹,才對堆有了一點理解。其定義為:堆是一種近似完全二叉樹的結構,並滿足堆性質,即子節點的鍵值索引總是小於(或大於)它的父節點。因此堆的任何一顆子樹仍然是堆。

 

  實驗說明

  堆排序的步驟為:

  a. 按照大堆或者小堆的方式將輸入進來的數組初始化為對應的堆。在此過程中先從最后一個非葉子節點開始慢慢后退到根節點,有點遞歸的味道,因為一旦一個節點的2個子節點樹都進行了堆初始化后,父節點和子節點即使不滿足堆對關系,也只需要調換其中一個位置再重新初始化過,而另一個子樹就無需堆初始化了(每個子節點的其中一個樹都無需堆初始化),這樣比較節省算法的速度。

  b. 交換堆中跟節點和最后一個未排序的節點的位置,然后繼續將參與排序的樹進行步驟a的堆形式初始化。

  產生不重復的隨機數,且需要滿足它的時間復雜度為O(n),這是本題比較刁難的地方。我這里采用方法是:對需排序的數組產生同樣大小的標記數組,如果對應位置的數字已經經過隨機數發生器產生過,則標志設置為1,否則設置為0,以后每當產生一個隨機數,只需判斷其對應位置的標志而已,這一操作的時間復雜度為O(1),所以產生n個不重復的隨機數的時間復雜度為O(n),其本質是利用空間來換取時間。

 

  C/c++知識總結:

  double floor ( double x );  

  該函數返回不大於x的最大整數,c/c++中有該函數的存在,如果x是正實數,則返回的整數字x去掉小數的部份。如果x是負實數,則返回的是x去掉小數點后繼續減1。

 

  實驗結果

  采用大端模式對10個數字進行堆排序的結果為:

  

 

  采用小端模式對10個數字進行堆排序的結果為:

  

 

  實驗代碼:

#include <iostream>
#include <vector>
#include <ctime>

using namespace std;

#define MAX_HEAP_MODE 0
#define MIN_HEAP_MODE 1

unsigned int heap_node_number = 20;
bool heap_sort_mode = MAX_HEAP_MODE;//默認為大堆排序模式
char choose_sort_mode; //手動輸入選擇小端大端模式的變量
vector<int> heap_tree;


/****
 *產生不重複的隨機數,且要滿足時間複雜度為O(n)
 *因此不能夠每次產生一個隨機數后,依次與前面已經產生過的隨機數做比較了,因為這樣的時間複雜度為O(n*n)
 *因此本函數採用的方法是以空間換時間,多建立一個vector,用來存儲對應的標誌位,如果對應數字已經產生了
 *則無需再產生,這樣的話每次判斷只有1下,即每次判斷的時間複雜度為O(1),產生n個隨機數的時間複雜度就為O(n)了
****/
void GenerateRandomNumber(vector<int> &create_rand_num, int num) {

    int count = 0; //記錄已經產生的滿足條件的隨機數的個數
    int sum_num  = num;   //保留所需的隨機數個數值
    srand( ( unsigned ) time( 0 ) );    //初始化隨機數種子,這樣的話每次產生的隨機數序列會有不同
    vector<bool> rand_flag(num, 0);     //以空間換時間的標記向量
    while(num) {
        int data = rand()%sum_num;  //產生0~sum_num之間的隨機數
        int temp_flag = rand_flag.at(data);     //將產生的隨機數序列放入向量中
        if(0 == temp_flag) {   //第一次出現
            create_rand_num.push_back(data); //將產生的非重複數據放入向量數組中
            rand_flag.at(data) = 1;  //並將其標誌位賦值為1
            count ++;
            num = sum_num - count ;     //還需要num個隨機數
        }
    }

}


/****
 *人機交互的輸入輸出初始化函數
****/
void Init() {

    cout << "請輸入堆排序節點的個數(1~50之間的整數): ";
    cin >> heap_node_number;
    if(heap_node_number > 50 || heap_node_number < 1) {
        cout << "輸入錯誤!請重新輸入1~50之間的整數:" << endl;
        Init() ;    //重新輸入排序的個數
        return ;
    }
    else
        cout << endl;
    cout << "請選擇堆排序模式(a)Max Mode, (b)Min Mode: ";
    cin >> choose_sort_mode;
    if(choose_sort_mode == 'a' || choose_sort_mode == 'A') {
        cout <<"當前堆排序模式為:大端模式" <<endl << endl;
        heap_sort_mode = MAX_HEAP_MODE;
    }
    else if(choose_sort_mode == 'b' || choose_sort_mode == 'B') {
        cout << "當前堆排序模式為:小端模式" << endl << endl;
        heap_sort_mode = MIN_HEAP_MODE;
    }

    GenerateRandomNumber(heap_tree, heap_node_number);
    cout << "二元樹的內容:" << endl ;
    for(int i = 0; i < heap_tree.size(); i++) {
        cout << "[" << heap_tree.at(i) <<"]  ";
    }
    cout << " " << endl << endl;

}


/****
 *輸出堆排序的中間過程
****/
void OutputHeapProcess(vector<int> &heap_tree, int display_mode) {

    if(0 == display_mode)
        cout << "堆積的內容為:" << endl;
    else if(1 == display_mode)
        cout << "重建的堆積為:" << endl;
    for(int i = 0; i < heap_tree.size(); i++)
        cout << "[" << heap_tree.at(i) << "]  ";
    cout << endl << endl;

}


/****
 *該函數的功能是:將heap_tree指定的位置節點select_node開始的子樹調整為堆
 *的形式,其結果仍然保留在heap_tree樹中。
 *該函數的調用必須配合select_node節點的子樹本身已經滿足堆的形式,也就是說該函數是
 *數heap_tree從底往上被調用的
****/
void AdjustChildHeapTree(vector<int> &heap_tree, int select_node, int len, bool heap_sort_mode) {

    int i = select_node;    //i為需要調整數的根節點在heap_tree的下標
    int child_num = 2*i + 1;   //i節點的左孩子的下標
    if(0 == heap_sort_mode) {
        while( child_num < len ) {   //子樹中所有的節點都必須調整
            if(child_num+1 < len && heap_tree.at(child_num)<heap_tree.at(child_num+1))
                child_num ++;   //如果右孩子的值大於左孩子的值,則將孩子的下標設置為右孩子的下標,其實就是選擇2個孩子中值較大的那個座標
            if(heap_tree.at(i) > heap_tree.at(child_num))
                break;  //如果根節點的值大於最大的孩子的值,則表示滿足大堆要求,直接退出
            else {
                /****交換父節點和子節點的位置****/
                int temp = heap_tree.at(child_num);
                heap_tree.at(child_num) = heap_tree.at(i);
                heap_tree.at(i) = temp;

                /****重新給父節點和子節點賦下標值****/
                i = child_num;
                child_num = 2*i +1;
            }
        }
    }
    else if(1 == heap_sort_mode) {
        while( child_num < len ) {   //子樹中所有的節點都必須調整
            if(child_num+1 < len && heap_tree.at(child_num)>heap_tree.at(child_num+1))
                child_num ++;   //如果右孩子的值大於左孩子的值,則將孩子的下標設置為右孩子的下標,其實就是選擇2個孩子中值較大的那個座標
            if(heap_tree.at(i) < heap_tree.at(child_num))
                break;  //如果根節點的值大於最大的孩子的值,則表示滿足大堆要求,直接退出
            else {
                /****交換父節點和子節點的位置****/
                int temp = heap_tree.at(child_num);
                heap_tree.at(child_num) = heap_tree.at(i);
                heap_tree.at(i) = temp;

                /****重新給父節點和子節點賦下標值****/
                i = child_num;
                child_num = 2*i +1;
            }
        }
    }
    return ;

}


void HeapSort(vector<int> &heap_tree, bool heap_sort_mode) {

    int len = heap_tree.size();

    /****
     *堆排序步驟1:
     *將heap_tree從最後一個非葉子節點開始初始化為堆的形式
    ****/
    for(int i = len/2; i >= 0; i--)
        AdjustChildHeapTree(heap_tree, i, len, heap_sort_mode);
    OutputHeapProcess(heap_tree, 0);

    /****
     *堆排序步驟2:
     *將根節點和最後一個節點調換位置
    ****/
    for(int i = 0; i < len-1; i++) {
        int temp = heap_tree.at(0);
        heap_tree.at(0) = heap_tree.at(len-i-1);
        heap_tree.at(len-i-1) = temp;

        AdjustChildHeapTree(heap_tree, 0, len-i-1, heap_sort_mode);//因為最後一個數已經是排序好了的,不需要參與下面的堆調整
        OutputHeapProcess(heap_tree, 1);
    }

}


int main() {

    Init();
    HeapSort(heap_tree, heap_sort_mode);
    return 0;
}

 

 

 

 

  參考文獻:

     http://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F#C.2B.2B.E8.AF.AD.E8.A8.80

 

 

 


免責聲明!

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



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