(二叉)堆,是乙個陣列,可以被看成乙個近似的完全二叉樹,樹上的每乙個節點對應於陣列中的乙個元素。除了最底層之外,該樹是完全充滿的,而且是從左向右填充,a.length表示陣列元素的個數,樹的根結點是a[1],
這樣給定乙個節點的下標i,我們很容易計算出其父節點(i / 2),左子女(2 * i),右子女(2 * i + 1);二叉堆又分為兩種形式:最大堆(也稱為大根堆)和最小堆(也稱為小根堆)。在最大堆中,最大堆性質是指除了根以外的所有節點i都滿足: a[parent(i)] >= a[i] (即就是根結點的值比左右子女的值都大);最小堆則恰恰相反,最小堆性質是指除了根以外的所有節點i都有:a[parent(i)] <= a[i], (即就是最小的元素存放在根結點)。
在堆排序演算法中,我們使用的是最大堆,最小堆通常用於構造優先佇列。
如圖則分別表示了最大堆和最小堆。
1.首先借助佇列按層建立出乙個完全二叉樹,2.對資料域的值用非遞迴方式調整成乙個大根堆,3.在進行堆排序; 第二種則是比較簡單的一種方式,所有的操作都在陣列上進行,利用下標可以將陣列邏輯看成乙個完全二叉樹,然後用遞迴方式調整成乙個堆,最後進行排序。總的來說,第二種方式比較好(演算法導論上也是這麼介紹的)。
先來看第一種的實現:
heapsort.h 和heapsort.c 的具體實現如下:
#ifndef _heap_h
#define _heap_h
#define n 10
typedef struct nodeheapnode;
//建立完全二叉樹
heapnode *creat_tree(int data,int n,heapnode ** q);
//堆排序
void heapsort(heapnode *root,heapnode **q,int n);
//列印
void print(heapnode ** q,int n);
#endif
#include #include #include "tools.h"
#include "heapsort.h"
//建立完全二叉樹
heapnode *creat_tree(int data,int n,heapnode ** q)
elseelse
}//入隊
q[++real] = newp;
}return root;
}//堆排序
void heapsort(heapnode *root,heapnode **q,int n)
if(q[pa] ->right && q[pa] ->data < q[pa] ->right ->data)
pa--;
}if(tag == 0)
}//交換
t = q[1] ->data;
q[1] ->data = q[r] ->data;
q[r] ->data = t;
if(q[r / 2] ->right)else
r--;
}}//列印
void print(heapnode ** q,int n)
}
主函式:
#include #include #include "heapsort.h"
#include "tools.h"
int main(int argc,char** agrv)
; int n = sizeof(a) / sizeof(a[0]);
q = (heapnode **)malloc(sizeof(heapnode *) * (n + 1));
root = creat_tree(a,n,q);
printf("排序前:\n");
print(q,n);
printf("\n");
heapsort(root, q ,n);
printf("排序後:\n");
print(q,n);
printf("\n");
return 0;
}
tools.h 和tools.c 的實現如下:
#ifndef _tools_h_
#define _tools_h_
#include #include //定義布林型別
#define true (1)
#define false (0)
typedef unsigned char boolean;
//定義介面
void *malloc(size_t size);
void *realloc(void * ptr,size_t size);
void print_int(void *value);
#endif
#include #include #include "tools.h"
void *malloc(size_t size)
return result;
}void *realloc(void * ptr,size_t size)
return result;
}void print_int(void *value)
其中q為乙個動態申請的佇列,creat_tree為建立完全二叉樹的過程,(借助於佇列),heapsort為堆排序的過程,首先將建立好的二叉樹調整成乙個完全二叉樹,其調整過程為乙個非遞迴。接下來,我們看程式的執行結果:
雖然實現了排序過程,但是我們是完全沒有必要建立出二叉樹,我們完全可以當成是邏輯上的二叉樹。
接下來我們看第二種實現方式:
heapsort.h 和heapsort.c的具體實現如下:
#ifndef _heap_h
#define _heap_h
#define n 10
//調整為大根堆的過程
void max_heapify(int *data,int i, int n);
//從無序的輸入資料陣列中構造乙個最大堆
void build_max_heap(int *data,int n);
//堆乙個陣列進行原址排序
void heap_sort(int *data,int n);
//列印陣列
void print_array(int *data,int n);
#endif
#include #include #include "tools.h"
#include "heapsort.h"
static void swap(int *a,int *b)
static int left(int i)
static int right(int i)
static int parent(int i)
//調整為大根堆
void max_heapify(int *data,int i,int n)
else
if(right <= length && data[right - 1] > data[largest - 1])
if(largest != i)
}void build_max_heap(int *data,int n)
}void heap_sort(int *data,int n)
}void print_array(int *data,int n)
printf("\n");
}
其中build_max_heap函式為邏輯上的大根堆,該函式呼叫了max_heapify函式,該函式主要是進行調整使得根結點的值大於左右子女的值,heap_sort為堆排序過程,遍歷陣列,將邏輯上的大根堆根節點的值和最後乙個葉子的值進行交換,則最大值已經位於陣列的尾部,然後長度減1,繼續呼叫max_heapify調整成大根堆,繼續迴圈。
接下來我們看主程式:
#include #include #include "heapsort.h"
#include "tools.h"
int main(int argc,char **argv)
printf("排序前:\n");
print_array(data,n);
heap_sort(data,n);
printf("排序後:\n");
print_array(data,n);
return 0;
}
執行結果如下圖:
演算法導論之堆排序
堆排序主要是先建堆,轉化為最大堆,每次把最大的乙個 即最大堆的根節點 和最後乙個交換,這樣每次都把當前最大的乙個放到了最後。堆排序演算法中,最關鍵的就是構造初始堆。需要編寫乙個維護大頂堆性質的函式max heapify。當輸入乙個陣列l和乙個下標i,然後呼叫max heapify時,比較l i l ...
演算法導論筆記之堆排序
堆排序 時間複雜度nlgn 堆是一種特殊的二叉樹,最大堆 根節點的key大於其兩個孩子節點鍵值,依次類推,所以最大值為根,最小值在葉子節點處 ita定義堆的陣列表示法為1 10,所以a 0 暫用無處,比如,長度為10的堆,則用a 1 a 10 表示,建立時的長度要比實際長度多1 幾個常見操作 hea...
演算法導論之堆排序相關
堆排序文件 二叉堆分為兩種 最大堆 最小堆 最大堆 最大元素在根部,在堆的所有結點中,a patent i a i 最小堆 最小元素在根部,在堆的所有結點中,a patent i a i 下面以最大堆為例解釋堆排序的具體過程 首先會用到幾個基本的函式 parent i return i 2 left...