最小堆数据结构与定时器实现

最小堆

最小堆的定义

1、是一棵满二叉树
2、子树也是一棵满二叉树
3、左右节点的值都比当前节点的大

从定义可知,根节点是树中的最小值,
最小堆一般用链表存储,父子节点的关系,用纯数学的关系表示为:假设当前节点的在链表中的索引为n,左子节点为2n+1 ,右子节点为2n+2
在这里插入图片描述

操作

对最小堆操作后,依然要保证树结构为最小堆
1、增加
在增加一个节点时,我们直接从链表末尾添加数据,同时对最后一个节点进行调整,如果比父节点小就需要为父节点交换位置,这就是“上升”操作
2、删除
在删除一个节点时,我们先找到当前节点,然后将其与未节点进行交换位置,这时候就需要对交换上的末尾节点进行调整,比子节点大就要与2个子节点中key最小的节点交换位置,这叫“下沉”操作,比父节点小就要进行“上升”操作

时间复杂度

增查时间复杂度为O( log ⁡ 2 n \log_2n log2n)
删除的时间复杂度为O(n)
最小值的查找时间复杂度为O(1)

定时器

作用

很显然就是定时的去执行任务,比如:应用层左的心跳检测,游戏里面的技能冷却

数据结构

当有大量的任务需要放入定时器中时,就需要考虑效率问题,主要体现在增加和删除上,所有需要选取一个高效的数据结构,一般来说选取最小堆作为定时器的存储数据结构,原因如下:

首先分析以下数据结构的效率:
1、红黑树:增删时间复杂度为O( log ⁡ 2 n \log_2n log2n)
2、跳表:增删时间复杂度为O( log ⁡ 2 n \log_2n log2n) ;对于跳表最⼩节点为最左侧节点,时间复杂度为O(1);但是空间复杂度⽐较⾼,为 O(1.5n);
3、最小堆:增时间复杂度为O( log ⁡ 2 n \log_2n log2n) ;删除时间复杂度为O(n),对于最小节点的获取,时间复杂度为O(1);

选择最小堆做数据结构,是因为会在使用最小堆的同时会辅以一个map的数据结构来快速找到节点,以解决删除效率低下的问题,这样他就是效率最高的数据结构了。

实现

设计
1、数据成员应该包含 一个 链表 vector和一个map
2、接口应包含增加和删除操作
3、以时间作为数据结构的key,value是待实现的任务

在这里我使用了模板类,以便用通用性

template <class T>
class Node{

public:
    int key;
    int idx;
    T t;
};
template <class T>
class MinHeap
{
public:
	void addNode(int key,T v);
	void removeNode(int key);
  	vector<Node<T> *> heap;
    map<int,Node<T>*> maps;
	...
}

增加节点的代码,先添加在上升调整,上升调整的目的是为了保证数据结构依然是最小堆

 void addNode(int key,T v)
    {
        Node<T> *node = new Node<T>();
        node->key = key;
        node->t = v;
        node->idx = heap.size();
        heap.push_back(node);

        shiftUp(heap.size()-1);
        maps.insert(make_pair(key,node));
    }

删除代码,通过map结构找到节点,然后从链表中删除,然后调整树结构(先下沉,在决定是否上升),同时将节点从map中移除

void removeNode(int key){

        if(maps.find(key) == maps.end())
            return;
        Node<T> *node = maps.at(key);
        int index = node->idx;
        int lastone = heap.size()-1;

        if(index != lastone){
            swap(heap[index],heap[lastone]);
            heap[index]->idx = index;
            if(!shiftDown(index))//下沉不成功则上升
                shiftUp(index);
        }
        heap.pop_back();
        maps.erase(key);
    }

值得一提的是:如果是插入相同的节点,对于定时器来说,可以考虑将节点的key 加一个很小很小的值,这样就不会存在一样的值了

结尾附上源码 以及qt的可视化界面
key为定时的时间戳,点击按钮添加节点时产生随机数自动填充最小堆,任务过期时会自动执行任务并删除节点
定时器可视化实现
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dai1396734

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

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

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

打赏作者

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

抵扣说明:

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

余额充值