悉野小楼

导航

二叉堆例子

参考: https://blog.csdn.net/lyqzzy/article/details/59849252 

https://baike.baidu.com/item/二叉堆

二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。

二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

 

用处: 系统进程优先级, 游戏处理时间回调, A星寻路的openset弹值.

例子: 下面是个小堆, 定义一个线程结构体, 先加些数据, 然后pop时拿出优先级最高的, 优先值为0的.

例子中UpHeap与DownHeap应改为循环处理, 不要使用递归.

#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
struct ProcessInfo
{
    int processId; //进程ID
    int priority; //优先级越小, 越在在前面
    void InfoOut()
    {
        cout << "pid:" << processId << ", priority:" << priority << endl;
    }
};

class MyHeap
{
public:
    shared_ptr<ProcessInfo> Pop()
    {
        //第一个最小, 就是我们要的
        auto retPtr = m_vects[0];
        //移除最小的操作, 数组第一与最后一个交换, 然后移除最后一个
        Swap(0, m_vects.size() - 1);
        m_vects.pop_back();
        //最左边的节点要向下沉
        DownHeap(0);
        return retPtr;
    }

    //数据放到最后
    void Push(shared_ptr<ProcessInfo> process)
    {
        m_vects.push_back(process);
        UpHeap(m_vects.size() - 1);
    }

    void Remove(shared_ptr<ProcessInfo> process)
    {
        int index = m_vects.size();
        for (int i = 0; i < m_vects.size(); ++i)
        {
            if (process->processId == m_vects[i]->processId)
            {
                index = i;
                //交换到最后
                Swap(index, m_vects.size() - 1);
                m_vects.pop_back();
                break;
            }
        }
        if (index >= m_vects.size())
        {
            return;
        }
        //交换后数据要下沉
        DownHeap(index);
    }

    //上浮 当前节点与父节点比较, 比父节点小时就上浮
    void UpHeap(int index)
    {
        if (index == 0)
        {
            return;
        }
        //子节点的值小于父节点的值, 向上冒, 交换, 否则停止
        int parentIndex = (index - 1) / 2;
        if (m_vects[index]->priority < m_vects[parentIndex]->priority)
        {
            Swap(index, parentIndex);
            //交换后, 节点到了父节点的位, 需要再检测, 是否继续向上冒了
            UpHeap(parentIndex);
        }
    }

    //下沉 当前节点与两个子点比较, 与最小的子节点交换
    void DownHeap(int index)
    {
        int childLeft = 2 * index + 1;
        if (childLeft >= m_vects.size())
        {
            //左子节点已经超出范围了
            return;
        }
        //找到最小的子节点
        auto minChild = m_vects[childLeft];
        int childRight = childLeft + 1;
        int minIdx = childLeft;
        if (childRight < m_vects.size() && minChild->priority > m_vects[childRight]->priority)
        {
            minIdx = childRight;
            minChild = m_vects[childRight];
        }
        if (m_vects[index]->priority < minChild->priority)
        {
            // 已经比两个子节点更小了, 中断下潜
            return;
        }
        // 当前节点与最小的子节点交换
        Swap(index, minIdx);

        //当前节点到了minIdx的位置了, 再次检测
        DownHeap(minIdx);
    }

    //交换两个节的值
    void Swap(int indexA, int indexB)
    {
        auto tmp = m_vects[indexA];
        m_vects[indexA] = m_vects[indexB];
        m_vects[indexB] = tmp;
    }
    int GetSize() { return m_vects.size(); }

    void SetCapcity(int capcity) { m_vects.reserve(capcity); }

    void PrintHeap()
    {
        cout << "heap size:" << m_vects.size() << ", info begin:------>\n";
        for (auto& ptr : m_vects)
        {
            ptr->InfoOut();
        }
        cout << "<------info end\n" << endl;
    }

    shared_ptr<ProcessInfo> GetDataByProcessID(int processId) 
    {
        for (auto& ptr : m_vects)
        {
            if (ptr->processId == processId)
            {
                return ptr;
            }
        }
    }
private:
    //小堆, 最顶端最小
    //对应数组就是最左边最小 第n个节点的子节点是第2n及2n+1位置 1的子节点为2与3节点, 2的子节点为2*2=4, 5, 3的子节点为6,7
    vector<shared_ptr<ProcessInfo>> m_vects;
};


int main()
{
    int capcity = 25;
    MyHeap heap;
    heap.SetCapcity(capcity);
    cout << "start push:" << endl;

    for (int i = 0; i < capcity; i++)
    {
        auto process = make_shared<ProcessInfo>();
        process->processId = i;
        process->priority = i % 5;
        process->InfoOut();
        heap.Push(process);
    }
    cout << "end push" << endl;

    //测试remove函数
    auto ptr = heap.GetDataByProcessID(15);
    heap.Remove(ptr);

    cout << "start pop heap:" << endl;
    while (heap.GetSize() > 0)
    {
        auto ptr = heap.Pop();
        ptr->InfoOut();
    }
    system("pause");
    return 0;
}

 

posted on 2023-08-30 19:20  悉野  阅读(3)  评论(0编辑  收藏  举报