33 排序演算法(6) 堆排序

2021-08-16 09:59:04 字數 2293 閱讀 8602

之前所講的演算法,很多都是 o(

n2) 的,造成這些演算法複雜度較高的主要原因是它們一次又一次地重複地進行這比較操作,並沒有利用之前的比較得到的結果。希爾排序對直接插入排序進行改進,使得這種方法的複雜度從 o(

n2) 下降到 o(

nlog

n)。在這裡我們將接著介紹一種利用之前排序結果的方法,堆排序。

若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹,如下圖就是乙個二叉樹

他並不一定保證所有的父節點都同時擁有左孩子和右孩子,如上圖的這種情況也是滿足定義的,但是如下圖的這種情況卻是不滿足定義的

堆是具有以下性質的完全二叉樹,每個結點的值都大於或等於其左右孩子節點的值,稱之為大定堆;相反地可以得到小頂堆,如下圖所示為大定堆和小頂堆

根結點一定是堆中所有結點最大或者最小者,如果按照層序遍歷的方式給結點從1開始編號,則結點之間滿足如下關係:

下標i與2i和2i+1是雙親和子女關係。那麼把大頂堆和小頂堆用層序遍歷存入陣列,則一定滿足上面的表示式。

堆排序(heap sort)就是利用堆進行排序的演算法,它的基本思想是:

(1) 將待排序的序列構造成乙個大頂堆(或小頂堆)。

(2) 此時,整個序列的最大值就是堆頂的根結點。將它移走(就是將其與堆陣列的末尾元素交換,此時末尾元素就是最大值)。

(3) 然後將剩餘的n-1個序列重新構造成乙個堆,這樣就會得到n個元素中的此大值。

(4) 如此反覆執行,便能得到乙個有序序列了。

#include 

int count = 0;

void swap(int k, int i, int j) //將陣列 k 中下標為 i 和 j 的元素進行互換

void heapadjust(int k, int s, int n) //調整父節點與孩子結點之間的位置

if( temp >= k[i] )

k[s] = k[i];

s = i;

}k[s] = temp;

}void heapsort(int k, int n)

// 利用堆進行排序

for( i=n; i > 1; i-- )

}int main()

; heapsort(a, 9);

printf("總共執行 %d 次比較!", count);

printf("排序後的結果是:");

for( i=1; i < 10; i++ )

printf("\n\n");

return

0;}

在上述的**中,只有子函式heapadjust(int k, int s, int n)較為難理解,他的過程實際上就是調節乙個雙親結點和孩子節點之間順序的作用,如果某乙個孩子節點和雙親結點發生了互換,還要討論互換後對下面的結點的影響。

由原始陣列中的數字直接構成的二叉樹如下所示

比如說當i= 1的時候,執行的結果如下圖所示,將最末端的數進行了重新排序,使其滿足最大頂的要求

比如說當i= 2的時候,執行的結果如下圖所示

比如說當i= 3的時候,執行的結果如下圖所示

這個時候不僅僅將以 2 號結點為根節點的小樹的順序進行了互換,還將以 4 號結點為根節點的小樹的順序進行了互換,這個是十分重要的。

(6)排序之堆排序

文章 靜默空間 堆的概念 在介紹堆排序之前,首先需要說明一下,堆是個什麼玩意兒。堆是一棵順序儲存的完全二叉樹。其中每個結點的關鍵字都不大於其孩子結點的關鍵字,這樣的堆稱為小根堆。其中每個結點的關鍵字都不小於其孩子結點的關鍵字,這樣的堆稱為大根堆。舉例來說,對於n個元素的序列當且僅當滿足下列關係之一時...

5排序 5堆排序

include using namespace std int n 10 元素個數 int b 11 定義全域性陣列 void shift down int i else flag 1 下乙個移動的結點是i,即沒有被更改 函式原理 對傳入的i進行下移,i移動到比它的兒子都小為止 最壞情況是移動到葉 ...

排序演算法6 堆排序

堆排序可以看作是簡單選擇排序的一種的改進方法,平均複雜度為 o n log n 因此應用場合較多。其原理同簡單選擇排序相似 將資料分為已排序和未排序的兩部分,並且不斷的從未排序資料中選取最大 或最小 資料加入到已排序集合中。不同之處在於,堆排序採用了一種特殊的二叉堆結構來快速的尋找最大值。如下圖,首...