何爲二叉堆?
二叉堆嘛,就是特化的二叉樹而已。
二叉堆又分爲最大堆和最小堆。
何爲最大堆
最大堆嘛,任意父節點大等於任何一個子節點的堆。
何爲最小堆
最小堆嘛,參考最大堆。
惡補基礎文章推薦
好,閒話不多說,如果對二叉樹還不瞭解的話,建議先了解一下
以下內容引起極度舒適,建議先把基礎打好。
二叉堆的插入
下面皆以最大堆說事兒
插入完全二叉樹的最後一個位置,然後不斷和父節點比較,不斷上浮,指導小於父節點爲止。
二叉堆的刪除
刪除是刪除堆頂,別問爲什麼,就是刪堆頂。
刪完之後,爲了完全二叉樹的完整性,將最後一個元素放到堆頂,然後不斷比較下沉。(往大的那邊沉)
構建二叉堆
本質就是將一棵無序的二叉樹通過浮沉構建一棵有序的堆樹。
還是說最大堆,從最後一個非葉子節點開始,依次下沉。
再說一下最小堆,從最後一個非葉子節點開始,依次上浮。
堆排序代碼
#include<stdio.h>
void swap(int a[],int i,int j)
{
int temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
//調整頂堆 使之變成大頂堆或小頂堆
//n是堆中個數 i是根節點
void heapify(int tree[], int n,int i)
{
if(i>=n)
{
return;
}
int c1 = 2 * i + 1;//左節點
int c2 = 2 * i + 2;//右節點
int max = i;
if ((c1<n) && (tree[c1] > tree[max]))
{
max = c1;
}
if ((c2<n) && (tree[c2] > tree[max]))
{
max = c2;
}
//如果最大值不是在根節點
if (max != i)
{
swap(tree, max, i);
heapify(tree, n, max);
}
}
//建造堆
void build_heap(int tree[], int n)
{
//找到堆中最後一個根結點
int last_node = n - 1;
int parent = (last_node - 1) / 2;
int i;
//將這個堆每個根結點進行堆排,則可實現變成一個堆
for (i = parent; i >= 0; i--)
{
heapify(tree, n, i);
}
}
//堆排序分類
void heap_sort(int tree[], int n)
{
build_heap(tree, n);
int i;
//i是最後一個結點
for (i = n - 1; i >= 0; i--)
{
//將最後一個結點和第一個結點進行交換
swap(tree, i, 0);
heapify(tree, i, 0);
}
}
int main05()
{
int tree[] = {2,5,3,1,10,4};
int n = 6;
printf_s("堆排序後\n");
//heapify(tree, n, 0);
//build_heap(tree, n);
heap_sort(tree,n);
for (int i = 0; i < n; i++) {
printf("%d\n", tree[i]);
}
return 0;
}
時空複雜度
空:O(1)
時:O(logn)
平均時間複雜度O(nlogn)
堆排序VS快速排序
要是對快速排序不熟,別怕,我也有:通俗點聊聊算法 - 快速排序(親測)
堆排序和快速排序的平均時間複雜度都是 O(nlogn),且都是不穩定排序。
不過,堆排序的最壞時間複雜度穩定在O(nlogn),快速排序的最壞時間複雜度爲O(n^2)。
此外,快速排序的空間複雜度更高,爲O(nlogn)。
爲什麼我更傾向於快速排序?
因爲上面的比對是基於堆建立完的情況下。如果一次排序多次取最值,我會考慮堆排序。注意,多次、最值!
是不是感覺節奏太快啦,我也覺着快了點。小場面,沒事