C語(yǔ)言實(shí)現(xiàn)基于最大堆和最小堆的堆排序算法示例
堆定義
堆實(shí)際上是一棵完全二叉樹(shù),其任何一非葉節(jié)點(diǎn)滿(mǎn)足性質(zhì):
Key[i]<=key[2i+1]&&Key[i]<=key[2i+2](小頂堆)或者:Key[i]>=Key[2i+1]&&key>=key[2i+2](大頂堆)
即任何一非葉節(jié)點(diǎn)的關(guān)鍵字不大于或者不小于其左右孩子節(jié)點(diǎn)的關(guān)鍵字。
堆排序的思想
利用大頂堆(小頂堆)堆頂記錄的是最大關(guān)鍵字(最小關(guān)鍵字)這一特性,使得每次從無(wú)序中選擇最大記錄(最小記錄)變得簡(jiǎn)單。
- 最大堆:所有節(jié)點(diǎn)的子節(jié)點(diǎn)比其自身小的堆。
- 最小堆:所有節(jié)點(diǎn)的子節(jié)點(diǎn)比其自身大的堆。
這里以最大堆為基礎(chǔ),其基本思想為:
1.將初始待排序關(guān)鍵字序列(R1,R2....Rn)構(gòu)建成大頂堆,此堆為初始的無(wú)序區(qū);
2.將堆頂元素R[1]與最后一個(gè)元素R[n]交換,此時(shí)得到新的無(wú)序區(qū)(R1,R2,......Rn-1)和新的有序區(qū)(Rn),且滿(mǎn)足R[1,2...n-1]<=R[n];
3.由于交換后新的堆頂R[1]可能違反堆的性質(zhì),因此需要對(duì)當(dāng)前無(wú)序區(qū)(R1,R2,......Rn-1)調(diào)整為新堆,然后再次將R[1]與無(wú)序區(qū)最后一個(gè)元素交換,得到新的無(wú)序區(qū)(R1,R2....Rn-2)和新的有序區(qū)(Rn-1,Rn)。不斷重復(fù)此過(guò)程直到有序區(qū)的元素個(gè)數(shù)為n-1,則整個(gè)排序過(guò)程完成。
C語(yǔ)言實(shí)現(xiàn)
1.基于最大堆實(shí)現(xiàn)升序排序
// 初始化堆
void initHeap(int a[], int len) {
// 從完全二叉樹(shù)最后一個(gè)非子節(jié)點(diǎn)開(kāi)始
// 在數(shù)組中第一個(gè)元素的索引是0
// 第n個(gè)元素的左孩子為2n+1,右孩子為2n+2,
// 最后一個(gè)非子節(jié)點(diǎn)位置在(n - 1) / 2
for (int i = (len - 1) / 2; i >= 0; --i) {
adjustMaxHeap(a, len, i);
}
}
void adjustMaxHeap(int a[], int len, int parentNodeIndex) {
// 若只有一個(gè)元素,那么只能是堆頂元素,也沒(méi)有必要再排序了
if (len <= 1) {
return;
}
// 記錄比父節(jié)點(diǎn)大的左孩子或者右孩子的索引
int targetIndex = -1;
// 獲取左、右孩子的索引
int leftChildIndex = 2 * parentNodeIndex + 1;
int rightChildIndex = 2 * parentNodeIndex + 2;
// 沒(méi)有左孩子
if (leftChildIndex >= len) {
return;
}
// 有左孩子,但是沒(méi)有右孩子
if (rightChildIndex >= len) {
targetIndex = leftChildIndex;
}
// 有左孩子和右孩子
else {
// 取左、右孩子兩者中最大的一個(gè)
targetIndex = a[leftChildIndex] > a[rightChildIndex] ? leftChildIndex : rightChildIndex;
}
// 只有孩子比父節(jié)點(diǎn)的值還要大,才需要交換
if (a[targetIndex] > a[parentNodeIndex]) {
int temp = a[targetIndex];
a[targetIndex] = a[parentNodeIndex];
a[parentNodeIndex] = temp;
// 交換完成后,有可能會(huì)導(dǎo)致a[targetIndex]結(jié)點(diǎn)所形成的子樹(shù)不滿(mǎn)足堆的條件,
// 若不滿(mǎn)足堆的條件,則調(diào)整之使之也成為堆
adjustMaxHeap(a, len, targetIndex);
}
}
void heapSort(int a[], int len) {
if (len <= 1) {
return;
}
// 初始堆成無(wú)序最大堆
initHeap(a, len);
for (int i = len - 1; i > 0; --i) {
// 將當(dāng)前堆頂元素與最后一個(gè)元素交換,保證這一趟所查找到的堆頂元素與最后一個(gè)元素交換
// 注意:這里所說(shuō)的最后不是a[len - 1],而是每一趟的范圍中最后一個(gè)元素
// 為什么要加上>0判斷?每次不是說(shuō)堆頂一定是最大值嗎?沒(méi)錯(cuò),每一趟調(diào)整后,堆頂是最大值的
// 但是,由于len的范圍不斷地縮小,導(dǎo)致某些特殊的序列出現(xiàn)異常
// 比如說(shuō),5, 3, 8, 6, 4序列,當(dāng)調(diào)整i=1時(shí),已經(jīng)調(diào)整為3,4,5,6,8序列,已經(jīng)有序了
// 但是導(dǎo)致了a[i]與a[0]交換,由于變成了4,3,5,6,8反而變成無(wú)序了!
if (a[0] > a[i]) {
int temp = a[0];
a[0] = a[i];
a[i] = temp;
}
// 范圍變成為:
// 0...len-1
// 0...len-1-1
// 0...1 // 結(jié)束
// 其中,0是堆頂,每次都是找出在指定的范圍內(nèi)比堆頂還大的元素,然后與堆頂元素交換
adjustMaxHeap(a, i - 1, 0);
}
}
2.基于最小堆實(shí)現(xiàn)降序排序
// 初始化堆
void initHeap(int a[], int len) {
// 從完全二叉樹(shù)最后一個(gè)非子節(jié)點(diǎn)開(kāi)始
// 在數(shù)組中第一個(gè)元素的索引是0
// 第n個(gè)元素的左孩子為2n+1,右孩子為2n+2,
// 最后一個(gè)非子節(jié)點(diǎn)位置在(n - 1) / 2
for (int i = (len - 1) / 2; i >= 0; --i) {
adjustMinHeap(a, len, i);
}
}
void adjustMinHeap(int a[], int len, int parentNodeIndex) {
// 若只有一個(gè)元素,那么只能是堆頂元素,也沒(méi)有必要再排序了
if (len <= 1) {
return;
}
// 記錄比父節(jié)點(diǎn)大的左孩子或者右孩子的索引
int targetIndex = -1;
// 獲取左、右孩子的索引
int leftChildIndex = 2 * parentNodeIndex + 1;
int rightChildIndex = 2 * parentNodeIndex + 2;
// 沒(méi)有左孩子
if (leftChildIndex >= len) {
return;
}
// 有左孩子,但是沒(méi)有右孩子
if (rightChildIndex >= len) {
targetIndex = leftChildIndex;
}
// 有左孩子和右孩子
else {
// 取左、右孩子兩者中最上的一個(gè)
targetIndex = a[leftChildIndex] < a[rightChildIndex] ? leftChildIndex : rightChildIndex;
}
// 只有孩子比父節(jié)點(diǎn)的值還要小,才需要交換
if (a[targetIndex] < a[parentNodeIndex]) {
int temp = a[targetIndex];
a[targetIndex] = a[parentNodeIndex];
a[parentNodeIndex] = temp;
// 交換完成后,有可能會(huì)導(dǎo)致a[targetIndex]結(jié)點(diǎn)所形成的子樹(shù)不滿(mǎn)足堆的條件,
// 若不滿(mǎn)足堆的條件,則調(diào)整之使之也成為堆
adjustMinHeap(a, len, targetIndex);
}
}
void heapSort(int a[], int len) {
if (len <= 1) {
return;
}
// 初始堆成無(wú)序最小堆
initHeap(a, len);
for (int i = len - 1; i > 0; --i) {
// 將當(dāng)前堆頂元素與最后一個(gè)元素交換,保證這一趟所查找到的堆頂元素與最后一個(gè)元素交換
// 注意:這里所說(shuō)的最后不是a[len - 1],而是每一趟的范圍中最后一個(gè)元素
// 為什么要加上>0判斷?每次不是說(shuō)堆頂一定是最小值嗎?沒(méi)錯(cuò),每一趟調(diào)整后,堆頂是最小值的
// 但是,由于len的范圍不斷地縮小,導(dǎo)致某些特殊的序列出現(xiàn)異常
// 比如說(shuō),5, 3, 8, 6, 4序列,當(dāng)調(diào)整i=1時(shí),已經(jīng)調(diào)整為3,4,5,6,8序列,已經(jīng)有序了
// 但是導(dǎo)致了a[i]與a[0]交換,由于變成了4,3,5,6,8反而變成無(wú)序了!
if (a[0] < a[i]) {
int temp = a[0];
a[0] = a[i];
a[i] = temp;
}
// 范圍變成為:
// 0...len-1
// 0...len-1-1
// 0...1 // 結(jié)束
// 其中,0是堆頂,每次都是找出在指定的范圍內(nèi)比堆頂還小的元素,然后與堆頂元素交換
adjustMinHeap(a, i - 1, 0);
}
}
3.C語(yǔ)言版測(cè)試
大家可以測(cè)試一下:
// int a[] = {5, 3, 8, 6, 4};
int a[] = {89,-7,999,-89,7,0,-888,7,-7};
heapSort(a, sizeof(a) / sizeof(int));
for (int i = 0; i < sizeof(a) / sizeof(int); ++i) {
NSLog(@"%d", a[i]);
}
上一篇:C基礎(chǔ) mariadb處理的簡(jiǎn)單實(shí)例
欄 目:C語(yǔ)言
下一篇:C/C++雜記 虛函數(shù)的實(shí)現(xiàn)的基本原理(圖文)
本文標(biāo)題:C語(yǔ)言實(shí)現(xiàn)基于最大堆和最小堆的堆排序算法示例
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/2271.html
您可能感興趣的文章
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用函數(shù)刪除字符
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)式函數(shù)庫(kù)
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)數(shù)怎么表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段函數(shù)
- 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排序法函數(shù)
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段函數(shù)
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎么打出三角函數(shù)的值
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求階乘


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 04-02jquery與jsp,用jquery
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?


