排序算法歸納(c語言) ⑤堆排序

堆排序

HeapSort,這是相當常用的一種排序算法。原因是它的時間複雜度相當穩定,且相當高效。無論是最好情況還是最壞情況,時間複雜度都能較穩定地保持在O(nlogn)
之所以這麼高效,是因爲它建立在一種特殊的數據結構上,即,堆。
什麼是堆呢?其實堆就是一個完全二叉樹
因爲是一個完全二叉樹,所以可以方便的用順序存儲的方式存下來。再根據完全二叉樹的特殊性質,可以很完美的實現堆的adjust
關鍵的性質
對於一棵有 n 個結點的完全二叉樹,其節點按層序編號,每層從左到右,則對於任一結點 i 有:

  • 如果 i 等於1,則節點 i 是二叉樹的根,無雙親。如果 i 大於1,則該節點的雙親是結點 i / 2。
  • 如果 2 * i 大於n,則該節點無左孩子,否則其左孩子是結點 2 * i 。
  • 如果 2 * i + 1 大於n,則該節點無右孩子,否則其右孩子是結點 2 * i + 1 。

堆排序的關鍵在於adjust,即保持這顆完全二叉樹是一個大頂堆(小頂堆)

  • 大頂堆:完全二叉樹中,任一結點都小於其雙親結點。
  • 小頂堆:完全二叉樹中,任一結點都大於其雙親結點。

我們每次取下一個堆頂元素,那麼只要每次操作完後,通過一次adjust,都能保證這顆完全二叉樹還是一個大頂堆(小頂堆),那麼我們取出的也就自然是有序序列了。

待排序數據依然存放於順序表中。
數據存放沒有從0開始,而是選擇從1開始。
代碼參考於《大話數據結構》。

初始設定

#include<stdio.h>
#define MAXSIZE 20 //順序表最大容量
#define N 10  //表中數據個數

順序表結構體

typedef struct
{
 int data[MAXSIZE + 1];
 int len;  //已存儲元素個數
}Sqlist;

輸出順序表

void Show(Sqlist L)
{
 int i;
 for (i = 1; i < L.len; ++i)
 {
  printf("%d,", L.data[i]);
 }
 printf("%d\n", L.data[i]);
}

輸入函數

void Input(Sqlist* lp)
{
 int d[N] = { 9, 1, 5, 8, 3, 0, 7, 4, 6, 2 };
 for (int i = 0; i < N; i++)
  lp->data[i + 1] = d[i];
 lp->len = N;
}

swap函數

void Swap(Sqlist* lp,int a, int b)
{   //交換順序表中兩元素的數值
 int t = lp->data[a];
 lp->data[a] = lp->data[b];
 lp->data[b] = t;
}

堆排序

void HeapAdjust(Sqlist* lp, int s, int m)
{
 int j, temp;
 temp = lp->data[s];	//暫存
 for (j = 2 * s; j <= m; j *= 2)
 {				//沿較大的孩子結點向下篩選
  if (j < m && lp->data[j] < lp->data[j + 1])
   ++j;			//將j標在較大的孩子上
  if (temp >= lp->data[j])
   break;			//說明此時該子樹雙親已經最大
  lp->data[s] = lp->data[j];	//較大孩子上移
  s = j;
 }
 lp->data[s] = temp;	//插入
}

void HeapSort(Sqlist* lp)
{
 int i;
 for (i = lp->len / 2; i > 0; --i)
 {		//不必考慮葉子節點,故一開始生成堆只需處理一半結點
  HeapAdjust(lp, i, lp->len);
 }
 for (i = lp->len; i > 1; --i)
 {		//每次將堆頂和最後一個元素交換,堆中元素個數減一
  Swap(lp, 1, i);
  HeapAdjust(lp, 1, i - 1);
 }
}

系列鏈接

上一個排序算法:
④希爾排序
下一個排序算法:
⑥歸併排序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章