主要測試大堆,測試源碼在(github)
堆
堆可以看成一個近似的完全二叉樹,除了底層外,該樹是完全充滿的,而且是從左到右填充。
完全二叉樹適合用數組來存儲。用數組來存儲完全二叉樹是非常節省存儲空間的。å
最大堆中,是指除了根結點外(根結點沒有 parent),所有結點的 i 都應該滿足:
堆被看作是一個完全二叉樹,那麼該堆堆高度成正比O(lgn),我們會發現,堆結構上的一些基本操作的運行時間至多與樹的高度成正比,即實際複雜度爲O(lgn)。
堆排序過程:
MAX-HEAPIFY(堆化):將堆的末端子節點作調整,使得子節點永遠小於父節點。
BUILD-MAX-HEAD(建堆):從無序數據數組中構造一個最大堆。
HEAPSORT(排序):對數組進行原址排序,移除位在第一個數據的根節點,並做最大堆調整的遞歸運算。
堆化
堆化讓數值大的結點往上浮,讓小的結點逐漸下沉。
void HeapSort::max_heapify(int array[], int size, int i) {
int largest = i;
int l = left(i);
int r = right(i);
if (l <= size && array[l] > array[i]) {
largest = l;
}
if (r <= size && array[r] > array[largest]) {
largest = r;
}
if (largest != i) {
swap(&array[largest], &array[i]);
max_heapify(array, size, largest);
}
}
建堆
我們可以通過二叉樹結點自底向上的方法,利用堆化過程,把一個大小爲 n = A.length 的數組 A = [1…n] 轉換爲最大堆。BUILD-MAX-HEAP 時間複雜度爲 O(n)
void HeapSort::build_max_heap(int array[], int len) {
for (int i = (len / 2); i >= 1; i--) {
max_heapify(array, len, i);
}
}
排序
BUILD-MAX-HEAP 會將數組A[1…n]建成最大堆。最大堆元素在根結點A[1],去掉 A[1] 後,A[1]的左右孩子結點仍然是最大堆。如果把 A[n] 替換 A[1],破壞了最大堆性質,將重新對 A[1…n-1] 數組進行堆化,使其變成最大堆。如此遞歸重複以上步驟。
void HeapSort::heap_sort() {
if (m_data_size <= 1) {
return;
}
// 建堆處理後,父結點 > 子結點
build_max_heap(m_array, m_data_size);
// 建堆後,堆頂結點(根結點)是最大數值,把最大值放到數組最後。原數組最後一個結點置換到根結點。
swap(&m_array[1], &m_array[m_data_size]);
// 排除數組最後一個元素,再對剩餘堆進行堆化,再把堆化的根結點放到數組最後。
m_data_size--;
// 從上到下(父節點到子樹結點)
while (m_data_size > 1) {
max_heapify(m_array, m_data_size, 1);
swap(&m_array[1], &m_array[m_data_size]);
m_data_size--;
}
}
優先隊列
在計算機系統的作業調度中,任務需要根據優先級進行執行。可以根據堆算法(最大堆),每個任務都賦予一個優先級數值,選出最高優先級(最大堆堆頂)作業任務執行。涉及任務處理,一般都有增刪改查的操作。
理解了建堆,堆化和排序的流程,隊列的這些操作應該都比較好理解了。
HEAP-MAXINUM
獲取堆頂元素,時間複雜度爲
bool HeapSort::heap_maxinum(int& n) {
if (m_data_size <= 0) {
return false;
}
if (!m_is_build_heap) {
build_max_heap(m_array, m_data_size);
}
n = m_array[1];
return true;
}
HEAP-EXTRACT-MAX
刪除堆頂元素,時間複雜度爲
bool HeapSort::heap_extract_max(int& n) {
if (m_data_size <= 0) {
return false;
}
if (!m_is_build_heap) {
build_max_heap(m_array, m_data_size);
}
n = m_array[1];
swap(&m_array[1], &m_array[m_data_size]);
m_data_size--;
max_heapify(m_array, m_data_size, 1);
return true;
}
HEAP-INCREAASE-KEY
增加堆指定元素(任務)的數值,HEAP-INCREAASE-KEY 時間複雜度爲
bool HeapSort::heap_increase_key(int i, int key) {
if (i < 1 || m_data_size <= 0 || key < m_array[i]) {
return false;
}
if (!m_is_build_heap) {
build_max_heap(m_array, m_data_size);
}
// 這裏跟 build_max_heap 道理一樣,只是 build_max_heap
// 是自底向上,heap_increase_key 是從 i 結點向上
m_array[i] = key;
while (parent(i) > 0 && m_array[parent(i)] < m_array[i]) {
swap(m_array[parent(i)], m_array[i]);
i = parent(i);
}
return true;
}
MAX-HEAP-INSERT
插入新元素到最大堆末位,也就是在最大堆上增加一個葉子,葉子自下而上與它的父結點比較替換。運行時間複雜度爲
bool HeapSort::max_heap_insert(int key) {
if (m_data_size >= m_data_len) {
return false;
}
if (!m_is_build_heap) {
build_max_heap(m_array, m_data_size);
}
// 將結點放置數組末位,也就是在最大堆上增加一個葉子,葉子自下而上與它的父結點比較替換。
m_data_size++;
m_array[m_data_size] = key;
// 這是 heap_increase_key 主邏輯的實現。
int i = m_data_size;
while (parent(i) > 0 && m_array[parent(i)] < m_array[i]) {
swap(m_array[parent(i)], m_array[i]);
i = parent(i);
}
return true;
}
參考
現在發現,單純看《算法導論》很難看懂文章內容,可以先從其它的帖子中理解算法的邏輯,在對算法有一定的理解的基礎上,再結合書本內容,才能更好理解書本內容。
wiki
《算法導論》第六章 堆排序
堆和堆排序:爲什麼說堆排序沒有快速排序快