排序演算法 堆排序演算法實現及分析

2021-08-30 06:45:33 字數 4573 閱讀 5304

堆排序(heap sort)就來利用堆(假設利用大頂堆)進行排序的方法。它的基本思想是,將待排序的序列構成乙個大頂堆。此時,整個序列的最大值就是堆頂的根結點。將它移走(其實就是將其與堆陣列的末尾元素交換,此時末尾元素就是最大值),然後將剩餘的n-1個序列重新構造成乙個堆,這樣就會得到n個元素中的次小值。如何反覆執行,便能得到乙個有序序列了。

定義看懂沒有?沒看懂沒關係,下面看**。

我們先來說說堆,我們這來看大頂堆,說大頂堆前,我先看乙個陣列如何模擬成樹的。請看下圖,將陣列元素從下標0開始,看做完全二叉樹結點,請以層序遍歷的角度看待它

我先來構造大頂堆,在構造堆之前,我們先看下堆的定義堆是具有下列性質的完全二叉樹每個結點的值都大於或等於其左右孩子結點的值,稱為大頂堆;或者每個結點的值都小於或等於其左右孩子結點的值,稱為小頂堆我們從後往前將每個結點構成乙個大頂堆不就得了?想想不是呢?因為最下層的結點已經滿足堆條件,然後調整上層結點,使其滿足大頂堆條件,那麼整個完全二叉樹不就成了大頂堆了麼。我們應該從後面的那個結點開始呢?當然是從非葉子結點 9 開始呀,因為葉子結點沒有孩子結點唄。將結點9看做一棵樹將其調整問堆,然後將3位根結點的樹調整為堆,然後將2為根結點的樹調整為堆........,整棵完全二叉樹就調整好了,成為了乙個大頂堆。請看下圖的**和說明。

將堆的初始化看懂了,就已經成功了一大半了。還有一小部分就是 將最值(下標為0)放到序列的尾部,然後將剩下的元素繼續進行堆調整,堆頂(下標為0)的元素 又是次最值,將其放到尾部,這樣一直迴圈 直到將每個元素放到堆頂,則堆排序完畢。

下面請堆排序**,然後再進行解說。

//堆調整 大堆頂,將最大值放在根結點

void

bigheadadjust

(int *arr,int index,int length)

if (rchildarr[max])

if (max != index)

return;

}//堆排序,採用大頂堆 公升序

void

heapsort_up

(int *arr, int length)

printf(

"大堆頂初始化順序:");

printarr(arr, length);

//將堆頂值放到陣列尾部,然後又進行大堆頂調整,一次堆調整最值就到堆頂了。

for (

int i = length -

1; i >=

0; i--)

return;

}

細心的朋友,會發現初始化化堆 是從非葉子結點從後往前進行調整為堆,而後序的堆調整 都是從下標為0處開始調整為堆。因為起初的完全二叉樹是完全無效的,所有只能從後往前調整。在初始化完堆之後,儘管將最值移動到尾部,打亂了堆,因為原來的堆結構已經基本形成,只需要遞迴呼叫bigheadadjust即可。

堆排序,它的執行時間主要是消耗在構建堆和在重建堆時的反覆篩選上。在構建堆的過程,因為我們是從完全二叉樹最下層的非葉子結點開始構建的,將它與其孩子結點進行比較和有必要的互換,對於每個非葉子結點來說,其實最多2次比較和互換,故初始化堆的時間複雜度為o(n)。在正式排序的時候,第i次取堆頂記錄和重建堆需要o(logi)的時間(完全二叉樹的某個結點到根結點的距離為log2i+1),並且需要取n-1次堆頂記錄,因此重建堆的時間複雜度為o(nlogn)。所以總的來說,堆排序的時間複雜度為o(nlogn)。由於堆排序對元素記錄的排序狀態不敏感,因此它無論最好,最壞,和平均時間複雜度均為o(nlogn)

#define _crt_secure_no_warnings

#include

#include

#include

#include

#define maxsize 1000000

//交換值

void

swap

(int* a, int* b)

//列印陣列元素

void

printarr

(int* arr, int length)

printf(

"\n");

return;

}long

getsystime

()

//堆調整 大堆頂,將最大值放在根結點

void

bigheadadjust

(int *arr,int index,int length)

if (rchildarr[max])

if (max != index)

return;

}//堆排序,採用大頂堆 公升序

void

heapsort_up

(int *arr, int length)

//printf("大堆頂初始化順序:");

//printarr(arr, length);

//將堆頂值放到陣列尾部,然後又進行大堆頂調整,一次堆調整最值就到堆頂了。

for (

int i = length -

1; i >=

0; i--)

return;

}//堆調整 小堆頂,將最小值放在根結點

void

smallheadadjust

(int *arr, int index, int length)

if (rchild

if (min != index)

return;

}//堆排序,採用小頂堆 降序

void

heapsort_down

(int *arr, int length)

for (

int i = length -

1; i >=

0; i--)

return;

}//希爾排序 公升序

//根據插入排序的原理,將原來的乙個大組,採用間隔的形式分成很多小組,分別進行插入排序

//每一輪結束後 繼續分成更小的組進行 插入排序,直到分成的小組長度為1,說明插入排序完畢

void

shellsort_up

(int* arr, int length)

arr[k + increase] = temp;}}

} while (increase>

1);}

intmain

(int argc, char *argv)

;int *arr2 = (

int*)

malloc(

sizeof(

int)*maxsize);

int *arr3 = (

int*)

malloc(

sizeof(

int)*maxsize);

//給每個元素設定乙個隨機值

for (

int i =

0; i < maxsize; i++)

//printf("排序前:\n");

//printarr(arr, 10);

//printf("堆排序公升序:\n");

//heapsort_up(arr, 10);

//printarr(arr, 10);

//printf("堆排序降序:\n");

//heapsort_down(arr, 10);

//printarr(arr, 10);

long start1 = getsystime();

shellsort_up(arr2, maxsize);

long end1 = getsystime();

printf(

"%d個元素 希爾排序耗費%d毫秒\n",maxsize,end1-start1);

long start2 = getsystime();

heapsort_up(arr3, maxsize);

long end2 = getsystime();

printf(

"%d個元素 堆排序耗費%d毫秒\n", maxsize, end2 - start2);

排序100萬個資料,比較下他們的效率。希爾排序居然比堆排序高,執行了好幾遍都是這個結果。。。

堆排序演算法及實現

使用陣列來模擬堆。由於是乙個滿二叉樹 左孩子使用nums 2i 1 模擬 右孩子使用nums 2i 2 模擬 堆排序的基本思路為 a.將無需序列構建成乙個堆,根據公升序降序需求選擇大頂堆或小頂堆 b.將堆頂元素與末尾元素交換,將最大元素 沉 到陣列末端 c.重新調整結構,使其滿足堆定義,然後繼續交換...

排序演算法之堆排序及Java實現

選擇排序 直接選擇排序,堆排序 交換排序 氣泡排序,快速排序 插入排序 直接插入排序,希爾排序 歸併排序 桶式排序 基數排序 堆排序是利用堆這種資料結構而設計的一種排序演算法,堆排序是一種選擇排序。堆是具有以下性質的完全二叉樹 每個結點的值都大於或等於其左右孩子結點的值,稱為大頂堆 或者每個結點的值...

堆排序演算法實現

include define true 1 define false 0 typedef struct recordtype void sift recordtype r,int k,int m 調整堆 r i t void crt heap recordtype r,int length 建立堆 ...