堆
堆是完全二叉樹,便於用array來儲存堆的所有節點;堆存儲在下標爲0開始計數的數組中,因此在堆中給定下標爲i的結點時:
① 如果i=0: 結點i是根節點,沒有雙親節點;否則結點i的雙親結點爲結點(i-1)/2。
② 如果2*i+1>n-1: 則結點i無左孩子,否則結點i的左孩子爲結點2*i+1。
③ 如果2*i+2>n-1: 則結點i無右孩子,否則結點i的右孩子爲結點2*i+2。
大根堆與小根堆priority_queue
根據元素排列方式,heap可分爲max-heap和min-heap兩種,前者每個節點的鍵值(key)都大於或等於其子節點鍵值,後者的每個節點鍵值(key)都小於或等於其子節點鍵值。因此,max-heap的最大值在根節點,並總是處於底層array或vector的頭部。min-heap的最小值在根節點,總是位於底層array或vector的起頭處。
堆排序
小根堆實現降序排列,大根堆實現升序排列。
小根堆實現堆排序(降序)基本思想:
(1) 初始化堆:將數列a[0,...,n-1]構成小根堆;
(2) 交換數據:將a[0]與a[n-1]交換,使a[n-1]成爲a[0,...,n-1]中的最小值;然後將a[0,...,n-2]重新調爲小根堆。然後,將a[0]與a[n-2]交換,使a[n-2]成爲a[0,...,n-2]中的最小值;然後將a[0,...,n-3]重新調爲小根堆。依次類對,直到整個數列a[0,...,n-1]有序(降序)。
代碼如下:
(1) 遞歸調整下沉
// 遞歸調整下沉
void minHeapAdjust(int *arr, int i, int size) // i爲父節點索引
{
int left = 2 * i + 1; // left爲左孩子節點索引
int right = 2 * i + 2; // right爲右孩子節點索引
int min = i; // min爲父節點、左節點、右節點中最小值的索引
if (i < size / 2) // 葉子節點不需調整,i>=size/2均是葉子節點
{
if (left < size&&arr[min] > arr[left])
min = left;
if (right < size&&arr[min] > arr[right])
min = right;
if (min != i) // 遞歸返回條件:父節點爲最小值則直接返回;
{ // 否則交換父節點與最小值節點,然後再繼續以min爲父節點索引,繼續遞歸調整;
swap(arr[min], arr[i]);
minHeapAdjust(arr, min, size);
}
}
}
// 建堆
void buildMinHeap(int *arr, int size)
{
for (int i = size / 2 - 1; i >= 0; i--)// 從第一次非葉子節點開始調整堆(索引爲size/2-1的節點爲第一個非葉子節點)
{
minHeapAdjust(arr, i, size);
}
}
// 堆排
void minHeapSort(int *arr, int size)
{
assert(arr != nullptr&&size > 0); // 檢查參數有效性
buildMinHeap(arr, size); // 建堆
for (int i = size - 1; i > 0; i--) // 將arr[0]與arr[n-1]交換,使arr[n-1]成爲arr[0,...,n-1]中的最小值;
{ // 然後將arr[0,...,n-2]重新調爲小根堆。
swap(arr[0], arr[i]); // 再將a[0]與a[n - 2]交換,使a[n - 2]成爲a[0, ..., n - 2]中的最小值;
minHeapAdjust(arr, 0, i); // 然後將a[0, ..., n - 3]重新調爲小根堆。依次類對,直到整個數列有序。
}
}
(2) 迭代調整下沉
// 迭代調整下沉
void adjustDown(int *arr, int i, int size)
{
int child = 2 * i + 1;
int min = i;
while (min < size / 2) // 當min>=size/2時,就到達非葉子節點了,非葉子節點有序,結束循環;
{
if (child + 1 < size&&arr[child] > arr[child + 1])
child++;
if (child<size&&arr[min]>arr[child])
{
swap(arr[min], arr[child]);
min = child;
child = 2 * min + 1;
}
else
break; // 父節點本身是min(父,左,右)時,跳出循環;
}
}
// 建立小根堆
void buildMinHeap(int *arr, int size)
{
for (int i = size / 2 - 1; i >= 0; i--)
adjustDown(arr, i, size);
}
// 堆排降序
void minHeapSort(int *arr, int size)
{
assert(arr != nullptr&&size > 0);
buildMinHeap(arr, size);
for (int i = size - 1; i > 0; i--)
{
swap(arr[0], arr[i]);
adjustDown(arr, 0, i);
}
}
優先級隊列priority_queue
priority_queue本質是一個堆。
1. 頭文件是#include<queue>
2. 關於priority_queue中元素的比較
模板申明帶3個參數:priority_queue<Type, Container, Functional>,其中Type 爲數據類型,Container爲保存數據的容器,Functional 爲元素比較方式。
Container必須是用數組實現的容器,比如vector,deque等等,但不能用 list。STL裏面默認用的是vector。
比較方式默認用operator<,所以如果把後面2個參數缺省的話,優先隊列就是大頂堆,隊頭元素最大。特別注意pair的比較函數:先按照pair的first元素降序,first元素相等時,再按照second元素降序。
如果要用到小頂堆,則一般要把模板的3個參數都帶進去。STL裏面定義了一個仿函數greater<>,基本類型可以用這個仿函數聲明小頂堆。如: priority_queue<int, vector<int>, greater<int> > q;