数据结构-堆

本文介绍了优先级队列的概念及基本操作,并详细探讨了大根堆和小根堆的构建、插入与删除过程,包括具体的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章中部分内容和思路来自《数据结构、算法与应用 c++语言描述》

预备知识

  • 优先级队列:是0个或多个元素的集合,每个元素都有一个优先级或值,对优先级队列执行的操作有:1)查找一个元素 2)插入一个新元素 3)删除一个元素,与这些操作相对应的函数是top,push和pop。在最小优先级队列中,查找和删除的元素都是优先级最小的元素;在最大优先级队列中,查找和删除的元素都是优先级最大的元素。优先级队列中的元素可以有相同优先级,这样的元素查找与删除顺序任意。
  • 大根(小根树):一棵大根(小根)树是这样一棵树,其中每个节点的值都大于(小于)或等于其子节点(如有)的值,如图:

堆定义

堆主要分为大根堆和小根堆,大根(小根)堆既是大根(小根)树,也是完全二叉树。如12-1(b) 虽是大根树,但不是大根堆。[ps:后续内容皆以大根堆为例]

堆构建

1.构建思路:将优先级数组构建成完全二叉树,再调整二叉树中各节点的位置。具体调整方法如图12-5,从最后一个有子树的节点开始将其调整成大根堆,如图12-5(a)中的[5],已经是大根堆,则不再调整;然后调整[4]节点,15<17则调换位置,如图12-5(b),以同样的方式调整3-2-1节点即可。

2.代码实现

template<typename T>
MaxHeap<T>::MaxHeap(T *arr, uint size)
{
    _heap = new T[size];
    memset(_heap, 0, sizeof(T) * size);
    memcpy(_heap, arr, sizeof(T) * size);
    _size = size;


    // 构建大根堆
    for (uint i = _size / 2 - 1; i >= 0; i--)
    {
        if (i == 0xffffffff)
            return;


        T rootEle = _heap[i];


        uint childIndex = 2 * i + 1;
        while(childIndex <= _size - 1)
        {
            if (childIndex < _size - 1 && _heap[childIndex] < _heap[childIndex + 1]) // 找到左右子节点中的大值
                childIndex++;


            // 已经符合大根堆
            if (rootEle >= _heap[childIndex])
                break;


            _heap[(childIndex - 1) / 2] = _heap[childIndex]; // 孩子上移
            childIndex = childIndex * 2 + 1; // 移到下一层
        }
        _heap[(childIndex - 1) / 2] = rootEle;
    }
}

堆插入

1.插入思路:把新元素插入到新节点,然后沿着从新节点到根节点的路径,执行一趟冒泡操作,将新元素与其父节点比较,直到后者大于或者等于前者为止[即先插入,再调整位置]。如插入数字1,插入后与其父节点比较大小,满足条件则不需要调整为止,如图12-3(b)所示。(c)、(d)图中分别插入5、21,图(c)执行一次调整位置,图(d)执行两次调整位置。

2.代码实现:

template<typename T>
void MaxHeap<T>::push(const T &node)
{
    T *newBlock = new T[_size + 1];
    memset(newBlock, 0, sizeof(T) * (_size + 1));
    memcpy(newBlock, _heap, sizeof(T) * _size);


    if (_heap)
    {
        delete []_heap;
        _heap = NULL;
    }


    _heap = newBlock;
    _size++;


    _heap[_size - 1] = node; // 增加新节点


    int nodeIndex = 0;
    for (nodeIndex = _size - 1; nodeIndex != 0 && _heap[(nodeIndex - 1) / 2] < node; nodeIndex /= 2)
    {
        _heap[nodeIndex] = _heap[(nodeIndex - 1) / 2];
    }
    _heap[nodeIndex] = node;
}

堆删除

1.删除思路:首先删除根元素[只删除值保留节点],然后删除最小叶子节点[取出值删除节点],最后调整节点顺序。如12-3(d)中删除一个元素,则如图(a)[根节点为20,20的节点填2]

2.代码实现

template<typename T>
void MaxHeap<T>::pop()
{
    if (0 == _size)
        return;


    T rootEle = _heap[_size - 1];


    uint nodeIndex = 0;
    uint childIndex = 2 * nodeIndex + 1;
    while(childIndex <= _size - 1)
    {
        if (childIndex < _size - 1 && _heap[childIndex] < _heap[childIndex + 1]) // 找到左右子节点中的大值
            childIndex++;


        // 已经符合大根堆
        if (rootEle >= _heap[childIndex])
            break;


        _heap[(childIndex - 1) / 2] = _heap[childIndex]; // 孩子上移
        nodeIndex = childIndex;
        childIndex = childIndex * 2 + 1; // 移到下一层
    }
    _heap[nodeIndex] = rootEle;


    T *newBlock = new T[_size - 1];
    memset(newBlock, 0, sizeof(T) * (_size - 1));
    memcpy(newBlock, _heap, sizeof(T) * (_size - 1));


    if (_heap)
    {
        delete []_heap;
        _heap = NULL;
    }


    _heap = newBlock;
    _size--;
}

 ====================================================================

博主有自己的个人主页啦!请求关注,请求支持。QAQ.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SuperYang_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值