通俗易懂,什么是二叉堆?

二叉堆

二叉堆是基于完全二叉树的基础上,加以一定的条件约束的一种特殊的二叉树。
根据约束条件的不同,二叉堆又可以分为两个类型:
大顶堆小顶堆

大顶堆

即任何一个父节点的值,都 大于等于 它左右孩子节点的值。

小顶堆

即任何一个父节点的值,都 小于等于 它左右孩子节点的值。

二叉堆的根节点叫做 堆顶 ,它是大顶堆里面的最大值,小顶堆里的最小值。

堆的操作

大顶堆和小顶堆的操作都是差不多的,这里用小顶堆来讲。

建立二叉堆

假如我们有一个二叉树,如下图:
在这里插入图片描述
我们从最下面一个 非叶子节点开始 ,是3。
因为小顶堆的父节点要比子节点要小,所以3可以不动,同理0也不动。
然后我们到了9,我们把9和它两个子节点中最小的那个换位置:
在这里插入图片描述
然后9继续和子节点交换:
在这里插入图片描述
这里其实做的是一个让非叶子节点 下沉 的操作。

然后我们接着把5下沉:
在这里插入图片描述
5接着下沉:
在这里插入图片描述
然后我们这个小顶堆就完成了。
堆的建立其实就是根据约束条件 下沉非叶子节点 的过程。

删除节点

堆节点的删除,是指将堆顶删除,如图:
在这里插入图片描述
因为二叉堆是建立在完全二叉树的基础上。
所以为了维护完全二叉树,我们将最后一个节点移动到堆顶:
在这里插入图片描述
然后我们根据约束规则,把9下沉就行了,最后结果是下图:
在这里插入图片描述

添加节点

建立和删除都是 下沉 操作,添加不一样,它做的是 上浮 操作。
假设我们要添加一个0,我们先将0最为最后一个节点,如图所示:
在这里插入图片描述
上浮 操作:
上浮节点 和其 父节点 比较,如果上 浮节点<父节点 ,就互换。
首先是0和6互换:
在这里插入图片描述
然后0和3换位置,再和2换位置,得到最终结果:
在这里插入图片描述

堆排序

堆排序非常简单。
因为堆顶是最值,所以我们可以把堆顶和最后一个节点换位置,然后再把最后节点从树上摘除。
此时我们得到了一个新的树,我们再把这个树重新变成二叉堆。
然后堆顶又是最值了,接着重复上述步骤,就能实现堆排序。

代码实现

因为堆是基于完全二叉树,所以我们不需要用链式结构来表示,我们可以直接用数组存。
假设父节点的下表为parent,从父节点获取子节点:
左节点下标: 2*parent+1
右节点下标: 2*parent+2
假设子节点的下标为son(左右子节点都可以):
父节点下标:(son-1)/2
所以我们可以非常简单的表示出节点之间的关系了,下面是代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000;
int heap[maxn];
int size = 0;
int getLeft(int parent) //获取左节点
{
    return 2 * parent + 1;
}
int getRight(int parent) //获取右节点
{
    return 2 * parent + 2;
}
int getParent(int son) //获取父节点
{
    if (son <= 0)
        return -1;
    return (son - 1) / 2;
}
void initHeap(int n) //n是堆的大小,初始化堆元素
{
    int index = n;
    while (index--) //获取最后一个非叶子节点
        if (getLeft(index) < n)
            break;
    while (index >= 0)
    {
        int parent = index, temp = heap[parent];
        while (true)
        {
            int left = getLeft(parent), right = getRight(parent);
            if (left >= n) //没有左节点
                break;
            int minSon = right >= n ? left : heap[left] < heap[right] ? left
                                                                      : right;
            if (temp <= heap[minSon]) //父节点小于等于最小子节点
                break;
            heap[parent] = heap[minSon];
            parent = minSon;
        }
        heap[parent] = temp;
        index--;
    }
}
void creatHeap(int n) //创建堆
{
    for (int i = 0; i < n; i++)
        cin >> heap[i];
    size = n;
    initHeap(size);
}
void insert(int value) //添加元素
{
    int son = size;
    heap[size++] = value; //先将插入的数据放在最后
    while (true)
    {
        int parent = getParent(son); //得到它的父节点
        if (parent < 0)              //son已经是根节点了,结束
            break;
        if (heap[parent] <= value) //父节点小于等于插入的值,结束
            break;
        heap[son] = heap[parent];
        //然后让父节点变成下一轮的子节点,向上继续比较
        son = parent;
    }
    heap[son] = value;
}
void pop() //删除元素
{
    if (size <= 0)
        return;
    heap[0] = heap[--size];
    int parent = 0, temp = heap[parent];
    while (true)
    {
        int left = getLeft(parent), right = getRight(parent);
        if (left >= size) //parent没有子节点了
            break;
        int minSon = right >= size ? left : heap[left] < heap[right] ? left
                                                                     : right;
        if (temp <= heap[minSon]) //父节点小于等于最小子节点
            break;
        heap[parent] = heap[minSon];
        parent = minSon;
    }
    heap[parent] = temp;
}
void printHeap() //打印堆
{
    for (int i = 0; i < size; i++)
        cout << heap[i] << ' ';
    cout << endl;
}
void heapSort() //堆排序
{
    if (size <= 0)
        return;
    int n = size;
    while (n--)
    {
        swap(heap[0], heap[n]);
        // printHeap();
        initHeap(n);
    }
}
  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值