演算法導論之堆排序

2021-07-09 23:09:28 字數 4943 閱讀 4253

(二叉)堆,是乙個陣列,可以被看成乙個近似的完全二叉樹,樹上的每乙個節點對應於陣列中的乙個元素。除了最底層之外,該樹是完全充滿的,而且是從左向右填充,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...