堆排序其實也是一種選擇排序,是一種樹形選擇排序。
只不過直接選擇排序中,爲了從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;
}