最小堆实现定时器

定时器之最小堆实现

选用最小堆的可行性

最⼩堆的性质有:

1. 是⼀颗完全⼆叉树;
2. 某⼀个节点的值总是⼩于等于它的⼦节点的值;
3. 堆中每个节点的⼦树都是最⼩堆;

应用到定时器的场景,我们只要按照每个任务触发的时间来对每一个堆节点(定时任务)进行组织,即可满足定时任务的顺序触发。

增加定时器即可通过在堆的尾部插入新的节点,时间复杂度插入为O(1),但是还得排序,故为O(logn)。

删除节点即可通过删除堆根节点,具体的删除操作为用最后一个定时器节点替换根节点,仍需要排序,时间复杂度为O(logn)。

最小堆相关结构设计

1.定义定时器结构

typedef void (*TimerHandler) (struct TimerNode *node);
// 定时器结构体
struct TimerNode {
    int idx = 0;                // 定时器在动态数组中的下标
    int id = 0;                 // 定时器id
    unsigned int expire = 0;    // 定时器触发时间戳
    TimerHandler cb = NULL;     // 定时触发执行函数,回调函数
};

2.定义最小堆类

// 最小堆类---组织定时器
class MinHeapTimer {
public:

    // 无参构造
    MinHeapTimer() {
        _heap.clear();
        _map.clear();
    }

    // 内联函数,计算定时器个数
    static inline int Count() {
        return ++_count;
    }

    // 添加定时器,参数为定时器触发的时间间隔,以及回调函数
    int AddTimer(uint32_t expire, TimerHandler cb);

    // 根据定时器id删除定时器
    bool DelTimer(int id);

    // 获取定时器的触发时间戳
    void ExpireTimer();

private:

    // 内联函数,用于比较堆数组中两个定时器的(时间戳)大小
    inline bool _lessThan(int lhs, int rhs) {
        return _heap[lhs]->expire < _heap[rhs]->expire;
    }

    // 下沉
    bool _shiftDown(int pos);

    // 上浮
    void _shiftUp(int pos);

    // 删除定时器
    void _delNode(TimerNode *node);

private:

    vector<TimerNode*>  _heap;          // 动态数组存储定时器
    map<int, TimerNode*> _map;          // id映射定时器,快速判断定时器是否存在
    static int _count;                  // 静态变量记录整个最小堆的定时器数量
};

int MinHeapTimer::_count = 0;           // 初始化静态变量

3.代码实现

int MinHeapTimer::AddTimer(uint32_t expire,     TimerHandler cb) {
    int64_t timeout = current_time() + expire;
    TimerNode* node = new TimerNode;
    int id = Count();
    node->id = id;
    node->expire = timeout;
    node->cb = cb;
    node->idx = (int)_heap.size();
    _heap.push_back(node);
    _shiftUp((int)_heap.size() - 1);
    _map.insert(make_pair(id, node));
    return id;
}

bool MinHeapTimer::DelTimer(int id)
{
    auto iter = _map.find(id);
    if (iter == _map.end())
        return false;
    _delNode(iter->second);
    return true;
}

void MinHeapTimer::_delNode(TimerNode *node)
{
    int last = (int)_heap.size() - 1;
    int idx = node->idx;
    if (idx != last) {
        std::swap(_heap[idx], _heap[last]);
        _heap[idx]->idx = idx;
        if (!_shiftDown(idx))  {
            _shiftUp(idx);
        }
    }
    _heap.pop_back();
    _map.erase(node->id);
    delete node;
}

void MinHeapTimer::ExpireTimer()
{
    if (_heap.empty()) return;
    uint32_t now = current_time();
    do {
        TimerNode* node = _heap.front();
        if (now < node->expire)
            break;
        for (int i = 0; i < _heap.size();  i++)
            std::cout << "touch    idx: " << _heap[i]   ->idx 
                << " id: " << _heap[i]->id << "     expire: "
                << _heap[i]->expire << std::endl;
        if (node->cb) {
            node->cb(node);
        }
        _delNode(node);
    } while(!_heap.empty());
}

bool MinHeapTimer::_shiftDown(int pos){
    int last = (int)_heap.size()-1;
    int idx = pos;
    for (;;) {
        int left = 2 * idx + 1;
        if ((left >= last) || (left < 0)) {
            break;
        }
        int min = left; // left child
        int right = left + 1;
        if (right < last && !_lessThan(left, right)) {
            min = right; // right child
        }
        if (!_lessThan(min, idx)) {
            break;
        }
        std::swap(_heap[idx], _heap[min]);
        _heap[idx]->idx = idx;
        _heap[min]->idx = min;
        idx = min;
    }
    return idx > pos;
}

void MinHeapTimer::_shiftUp(int pos)
{
    for (;;) {
        int parent = (pos - 1) / 2; // parent node
        if (parent == pos || !_lessThan(pos, parent)) {
            break;
        }
        std::swap(_heap[parent], _heap[pos]);
        _heap[parent]->idx = parent;
        _heap[pos]->idx = pos;
        pos = parent;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值