堆排序、選擇排序

堆排序其實也是一種選擇排序,是一種樹形選擇排序

只不過直接選擇排序中,爲了從R[1...n]中選擇最大記錄,需比較n-1次

然後從R[1...n-2]中選擇最大記錄需比較n-2次......

事實上這n-2次比較中有很多已經在前面的n-1次比較中已經做過

而樹形選擇排序恰好利用樹形的特點保存了部分前面的比較結果,因此可以減少比較次數。

對於n個關鍵字序列,最壞情況下每個節點需比較log2(n)次,因此其最壞情況下時間複雜度爲nlogn。

堆排序爲時間複雜讀度不穩定排序,不適合記錄較少的排序。

 

建堆以及堆排序的完整代碼如下:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int h[101];//用來存放堆的數組
int n;//用來存儲堆中元素的個數,也就是堆的大小
//向下調整函數,建立小頂堆 
void siftdown(int i) //傳入一個需要向下調整的結點編號i,這裏傳入1,即從堆的頂點開始向下調整
{
    int t,flag=0;//flag用來標記是否需要繼續向下調整
    //當i結點有兒子的時候(其實是至少有左兒子的情況下)並且有需要繼續調整的時候循環窒執行
    while(i*2<=n&&flag==0)
    {        
        //首先判斷他和他左兒子的關係,並用t記錄值較小的結點編號
        if(h[i]>h[i*2])t=i*2;         
        else t=i;          
        //如果他有右兒子的情況下,再對右兒子進行討論
        if(i*2+1<= n)  
            if(h[t] >h[i*2+1])//如果右兒子的值更小,更新較小的結點編號
                t=i*2+1;    
        //如果發現最小的結點編號不是自己,說明子結點中有比父結點更小的  
        if(t!=i){
            swap(h[t],h[i]);//交換它們
            i=t;//更新i爲剛纔與它交換的兒子結點的編號,便於接下來繼續向下調整
        }
        else flag=1;//則否說明當前的父結點已經比兩個子結點都要小了,不需要在進行調整了
    }
}
//建立堆的函數
void creat(){
    int i;
    //從最後一個非葉結點到第1個結點依次進行向上調整
    for(i=n/2;i>=1;i--)siftdown(i);        
}
//刪除最小的元素,並輸出 
int deletemax(){
    int t;
    t=h[1];//用一個臨時變量記錄堆頂點的值
    h[1]=h[n];//將堆得最後一個點賦值到堆頂
    n--;//堆的元素減少1
    siftdown(1);//向下調整
    return t;//返回之前記錄的堆頂點,數組的最小值
}
int main()
{
    int i,num;
    scanf("%d",&num);//讀入數的個數
    for(i=1;i<=num;i++)scanf("%d",&h[i]);    
    n=num;   
    //建堆
    creat();
    //刪除頂部元素,連續刪除n次,其實夜就是從小到大把數輸出來
    for(i=1;i<=num;i++)printf("%d ",deletemax());
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章