1. 堆
1.1 簡介
堆又稱二叉堆(由于其它幾種堆(二項式堆,斐波納契堆等)用的較少,一般將二叉堆就簡稱為堆),在結構上可以視為一棵完全的二叉樹(不過堆又增加了最大堆和最小堆的性質,下邊1.2會講)。完全二叉樹的一個“優秀”的性質是,除了最底層之外,每一層都是滿的,這使得堆可以利用數組來表示(普通的一般的二叉樹通常用鏈表作為基本容器表示),每一個結點對應數組中的一個元素:
Parent = floor((i-1)/2),i 的父節點下標
Left = 2i + 1,i 的左子節點下標
Right = 2i+ 2,i 的右子節點下標
n/2-1以及之前的都是父節點
1.2 二叉堆一般分為兩種:最大堆和最小堆:
- 最大堆:
最大堆中的最大元素值出現在根結點(堆頂)
堆中每個父節點的元素值都大于等于其孩子結點(如果存在)
- 最小堆:
最小堆中的最小元素值出現在根結點(堆頂)
堆中每個父節點的元素值都小于等于其孩子結點(如果存在)
2. 堆排序
堆排序主要分三步:
(1).構建堆
(2).調整堆
(3).堆排序
具體步驟:
- 1.將長度為n的待排序的數組進行堆有序化構造成一個大頂堆:從最后一個父節點開始(向前),將所有父節點都跟它的子節點比較,保證所有父節點大于子節點。
- 2.將根節點與尾節點交換并輸出此時的尾節點
- 3.將剩余的n -1個節點重新進行堆有序化,此時因為只有堆頂的數據是新換上來的,只需要它跟它的子節點比較,因為只有最上邊的是新換上來的,所以只需要跟子節點比較,一直往下沉即可。
- 4.重復步驟2,步驟3直至構造成一個有序序列
首先需要明確一點,構建堆是在數組基礎上構建的,換句話說就是將數組抽象成一個二叉堆,而不需要另構建。
在構建堆之前需要保證一點,構建之后的結構需要堆序性質。
代碼:
void swapValue(int *arr, int i, int j)
{
if (arr==NULL) {
return;
}
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//對index所在的節點,判斷是否有子節點,如果有則比較,并交換,保證該節點比子節點都大。如果產生交換了,則交換下去的節點還需要和它的節點做同樣的判斷交換。
void maxHeapify(int *arr, int index, int len)
{
if (arr==NULL || len<=0) {
return;
}
int left = 2*index+1;//因為是二叉堆,滿足完全二叉樹的結構,所以只判斷左子樹就知道有無,子節點了
int right = left+1;
int max = left;
while (left < len) {//證明有子節點
if (right<len && arr[right]>arr[left]) {
max = right;
}
if (arr[max]>arr[index]) {
swapValue(arr, max, index);
}
index = max;
left = 2*index+1;
right = left+1;
max = left;
}
}
//算法堆排序:代碼是升序
void heapSort(int *arr, int len)
{
if (arr==NULL || len<=0) {
return;
}
//1.構建堆(數組數據滿足堆序)
for (int i=len/2-1; i>=0; i--) {
maxHeapify(arr, i, len);
}
swapValue(arr, 0, len-1);//2.首尾交換
len--;
while (len>1) {
maxHeapify(arr, 0, len);//3.重新調整堆(滿足大頂堆),因為只有最上邊的是新換上來的,所以只需要跟子節點比較,一直往下沉即可
swapValue(arr, 0, len-1);//4.首尾交換
len--;
}
}