学习笔记--libevent min_heap
libevent采用小根堆管理其timer,所以了解heap的特性是必须的
小根堆的特性为:1完全二叉树
2子节点的值大于其根结点的值
其实根据这两点,就可以实现小根堆了。完全二叉树决定了如果已知子结点索引child_index,则父结点索引parent_index为(child_index-1)/2,反之
有了这些基础就可以看代码了
/*这个函数的目的是根据hole_index,判断e的插入位置*/
void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e)
{
/*找出父结点*/
unsigned parent = (hole_index - 1) / 2;/*当索引不为0(即根结点索引时且父结点的值大于e的值时)*/
while (hole_index && min_heap_elem_greater(s->p[parent], e)){
/*记住小根堆的特性,根结点的值必须小于子结点的值,所以当hole_index对应的值小于其父结点的值的时候,父结点下沉,即hole_index上浮*/
(s->p[hole_index] = s->p[parent])->ev_timeout_pos.min_heap_idx = hole_index;/*hole_index继续上浮*/
hole_index = parent;
parent = (hole_index - 1) / 2;}
/*跳出循环是说明条件已不再满足,所以此时可以插入e*/
(s->p[hole_index] = e)->ev_timeout_pos.min_heap_idx = hole_index;}
/*与上述过程相反,但实现复杂一点点*/
void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e)
{
/*找出子结点*/
unsigned min_child = 2 * (hole_index + 1);
/*如果没有到最后一个结点*/
while (min_child <= s->n){
/*虽然libevent的实现确实非常强,但是有些代码会让人一下看不出来,源码中有很多类似的实现*/
/*其实下面这句代码相当于(但不是等于,有一个地方s->n),完全二叉树的特性为如果有右子树,那么左子树一定不为空,min_child 的值是代表右子树的值,
因为右子树可能为空,所以需要与堆的长度判断一下,如果相等即有右子树,此时判断条件必为true,即min_child = min_child -1(为左子树的值)
if(s->p[min_child] > s->p[min_child - 1] )
{
min_child -= 1
}
*/
min_child -= min_child == s->n || min_heap_elem_greater(s->p[min_child], s->p[min_child - 1]);
/*如果e的值小于min_child ,跳出循环,已经找到需要插入的地方*/
if (!(min_heap_elem_greater(e, s->p[min_child])))break;
/*hole_index下沉*/
(s->p[hole_index] = s->p[min_child])->ev_timeout_pos.min_heap_idx = hole_index;hole_index = min_child;
min_child = 2 * (hole_index + 1);
}
(s->p[hole_index] = e)->ev_timeout_pos.min_heap_idx = hole_index;
}