何为二叉堆?
二叉堆嘛,就是特化的二叉树而已。
二叉堆又分为最大堆和最小堆。
何为最大堆
最大堆嘛,任意父节点大等于任何一个子节点的堆。
何为最小堆
最小堆嘛,参考最大堆。
恶补基础文章推荐
好,闲话不多说,如果对二叉树还不了解的话,建议先了解一下
以下内容引起极度舒适,建议先把基础打好。
二叉堆的插入
下面皆以最大堆说事儿
插入完全二叉树的最后一个位置,然后不断和父节点比较,不断上浮,指导小于父节点为止。
二叉堆的删除
删除是删除堆顶,别问为什么,就是删堆顶。
删完之后,为了完全二叉树的完整性,将最后一个元素放到堆顶,然后不断比较下沉。(往大的那边沉)
构建二叉堆
本质就是将一棵无序的二叉树通过浮沉构建一棵有序的堆树。
还是说最大堆,从最后一个非叶子节点开始,依次下沉。
再说一下最小堆,从最后一个非叶子节点开始,依次上浮。
堆排序代码
#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)。
为什么我更倾向于快速排序?
因为上面的比对是基于堆建立完的情况下。如果一次排序多次取最值,我会考虑堆排序。注意,多次、最值!
是不是感觉节奏太快啦,我也觉着快了点。小场面,没事