欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言實(shí)現(xiàn)基于最大堆和最小堆的堆排序算法示例

 更新時(shí)間:2016年06月08日 11:04:29   作者:黃儀標(biāo)  
這篇文章主要介紹了C語言實(shí)現(xiàn)基于最大堆和最小堆的堆排序算法示例,分別是基于最大堆的升序排序和基于最小堆的降序排序?qū)嵗?需要的朋友可以參考下

堆定義
堆實(shí)際上是一棵完全二叉樹,其任何一非葉節(jié)點(diǎ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)鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡(jiǎn)單。

  • 最大堆:所有節(jié)點(diǎn)的子節(jié)點(diǎn)比其自身小的堆。
  • 最小堆:所有節(jié)點(diǎn)的子節(jié)點(diǎn)比其自身大的堆。

這里以最大堆為基礎(chǔ),其基本思想為:

1.將初始待排序關(guān)鍵字序列(R1,R2....Rn)構(gòu)建成大頂堆,此堆為初始的無序區(qū);
2.將堆頂元素R[1]與最后一個(gè)元素R[n]交換,此時(shí)得到新的無序區(qū)(R1,R2,......Rn-1)和新的有序區(qū)(Rn),且滿足R[1,2...n-1]<=R[n];
3.由于交換后新的堆頂R[1]可能違反堆的性質(zhì),因此需要對(duì)當(dāng)前無序區(qū)(R1,R2,......Rn-1)調(diào)整為新堆,然后再次將R[1]與無序區(qū)最后一個(gè)元素交換,得到新的無序區(qū)(R1,R2....Rn-2)和新的有序區(qū)(Rn-1,Rn)。不斷重復(fù)此過程直到有序區(qū)的元素個(gè)數(shù)為n-1,則整個(gè)排序過程完成。

C語言實(shí)現(xiàn)
1.基于最大堆實(shí)現(xiàn)升序排序

// 初始化堆
void initHeap(int a[], int len) {
 // 從完全二叉樹最后一個(gè)非子節(jié)點(diǎn)開始
 // 在數(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è)元素,那么只能是堆頂元素,也沒有必要再排序了
 if (len <= 1) {
  return;
 }
 
 // 記錄比父節(jié)點(diǎn)大的左孩子或者右孩子的索引
 int targetIndex = -1;
 
 // 獲取左、右孩子的索引
 int leftChildIndex = 2 * parentNodeIndex + 1;
 int rightChildIndex = 2 * parentNodeIndex + 2;
 
 // 沒有左孩子
 if (leftChildIndex >= len) {
  return;
 }
 
 // 有左孩子,但是沒有右孩子
 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)所形成的子樹不滿足堆的條件,
  // 若不滿足堆的條件,則調(diào)整之使之也成為堆
  adjustMaxHeap(a, len, targetIndex);
 }
}
 
void heapSort(int a[], int len) {
 if (len <= 1) {
  return;
 }
 
 // 初始堆成無序最大堆
 initHeap(a, len);
 
 for (int i = len - 1; i > 0; --i) {
  // 將當(dāng)前堆頂元素與最后一個(gè)元素交換,保證這一趟所查找到的堆頂元素與最后一個(gè)元素交換
  // 注意:這里所說的最后不是a[len - 1],而是每一趟的范圍中最后一個(gè)元素
  // 為什么要加上>0判斷?每次不是說堆頂一定是最大值嗎?沒錯(cuò),每一趟調(diào)整后,堆頂是最大值的
  // 但是,由于len的范圍不斷地縮小,導(dǎo)致某些特殊的序列出現(xiàn)異常
  // 比如說,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反而變成無序了!
  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) {
 // 從完全二叉樹最后一個(gè)非子節(jié)點(diǎn)開始
 // 在數(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è)元素,那么只能是堆頂元素,也沒有必要再排序了
 if (len <= 1) {
  return;
 }
 
 // 記錄比父節(jié)點(diǎn)大的左孩子或者右孩子的索引
 int targetIndex = -1;
 
 // 獲取左、右孩子的索引
 int leftChildIndex = 2 * parentNodeIndex + 1;
 int rightChildIndex = 2 * parentNodeIndex + 2;
 
 // 沒有左孩子
 if (leftChildIndex >= len) {
  return;
 }
 
 // 有左孩子,但是沒有右孩子
 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)所形成的子樹不滿足堆的條件,
  // 若不滿足堆的條件,則調(diào)整之使之也成為堆
  adjustMinHeap(a, len, targetIndex);
 }
}
 
void heapSort(int a[], int len) {
 if (len <= 1) {
  return;
 }
 
 // 初始堆成無序最小堆
 initHeap(a, len);
 
 for (int i = len - 1; i > 0; --i) {
  // 將當(dāng)前堆頂元素與最后一個(gè)元素交換,保證這一趟所查找到的堆頂元素與最后一個(gè)元素交換
  // 注意:這里所說的最后不是a[len - 1],而是每一趟的范圍中最后一個(gè)元素
  // 為什么要加上>0判斷?每次不是說堆頂一定是最小值嗎?沒錯(cuò),每一趟調(diào)整后,堆頂是最小值的
  // 但是,由于len的范圍不斷地縮小,導(dǎo)致某些特殊的序列出現(xiàn)異常
  // 比如說,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反而變成無序了!
  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語言版測(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]);
}

相關(guān)文章

  • 簡(jiǎn)單總結(jié)C++中指針常量與常量指針的區(qū)別

    簡(jiǎn)單總結(jié)C++中指針常量與常量指針的區(qū)別

    這里我們來簡(jiǎn)單總結(jié)C++中指針常量與常量指針的區(qū)別,包括如何聲明和使用常量指針以及指針常量,需要的朋友可以參考下
    2016-06-06
  • C語言編程之掃雷小游戲空白展開算法優(yōu)化

    C語言編程之掃雷小游戲空白展開算法優(yōu)化

    掃雷是電腦上很經(jīng)典的游戲,特意去網(wǎng)上玩了一會(huì),幾次調(diào)試之后,發(fā)現(xiàn)這個(gè)比三子棋要復(fù)雜一些,尤其是空白展開算法上和堵截玩家有的一拼,與實(shí)際游戲差別較大,不能使用光標(biāo),下面來詳解每一步分析
    2021-09-09
  • C++超詳細(xì)講解運(yùn)算符重載

    C++超詳細(xì)講解運(yùn)算符重載

    本文包括了對(duì)C++類的6個(gè)默認(rèn)成員函數(shù)中的賦值運(yùn)算符重載和取地址和const對(duì)象取地址操作符的重載。運(yùn)算符是程序中最最常見的操作,例如對(duì)于內(nèi)置類型的賦值我們直接使用=賦值即可,因?yàn)檫@些編譯器已經(jīng)幫我們做好了,但是對(duì)象的賦值呢?能直接賦值嗎
    2022-06-06
  • C語言詳細(xì)分析講解內(nèi)存管理malloc realloc free calloc函數(shù)的使用

    C語言詳細(xì)分析講解內(nèi)存管理malloc realloc free calloc函數(shù)的使用

    C語言內(nèi)存管理相關(guān)的函數(shù)主要有realloc、calloc、malloc、free等,下面這篇文章主要給大家介紹了關(guān)于C語言內(nèi)存管理realloc、calloc、malloc、free函數(shù)的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • c++中的兩種getline用法詳解

    c++中的兩種getline用法詳解

    c++中有2種getline函數(shù),一種在頭文件 <istream> 中,是istream類的成員函數(shù);另一種是在頭文件 <string> 中,是普通函數(shù)。這篇文章主要介紹了c++中的兩種getline用法,需要的朋友可以參考下
    2020-02-02
  • C++基于EasyX實(shí)現(xiàn)簡(jiǎn)單掃雷游戲

    C++基于EasyX實(shí)現(xiàn)簡(jiǎn)單掃雷游戲

    這篇文章主要為大家詳細(xì)介紹了C++基于EasyX實(shí)現(xiàn)簡(jiǎn)單掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • C++核心編程之類和對(duì)象詳解

    C++核心編程之類和對(duì)象詳解

    這篇文章主要介紹了C++編程進(jìn)階之類和對(duì)象用法等相關(guān)使用技巧,需要的朋友可以參考下,希望能夠給你帶來幫助
    2021-09-09
  • 詳解Matlab如何繪制圓角半透明圖例

    詳解Matlab如何繪制圓角半透明圖例

    目前MATLAB的legend圖例是不支持圓角和半透明的,所以本文將自制實(shí)現(xiàn)圓角半透明圖例。文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-05-05
  • Visual C++ 常用數(shù)據(jù)類型轉(zhuǎn)換方法詳解

    Visual C++ 常用數(shù)據(jù)類型轉(zhuǎn)換方法詳解

    本文純粹是總結(jié)一下有關(guān)類型轉(zhuǎn)換的貼子,需要的朋友可以參考下
    2017-06-06
  • Linux C 時(shí)間函數(shù)應(yīng)用

    Linux C 時(shí)間函數(shù)應(yīng)用

    本文是關(guān)于Linux C時(shí)間函數(shù) time_t struct tm 進(jìn)行了詳細(xì)的分析介紹并有應(yīng)用實(shí)例,希望能幫到有需要的同學(xué)
    2016-07-07

最新評(píng)論