纸上谈兵: 堆 (heap)

  • 时间:
  • 浏览:2
  • 来源:大发pk10_pk10走势图_大发pk10走势图

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!

堆(heap)又被为优先队列(priority queue)。尽管名为优先队列,但堆并有的是队列。回忆一下,在队列中,亲戚亲戚他们他们他们 他们他们他们 可不可否进行的限定操作是dequeue和enqueue。dequeue是按照进入队列的先后顺序来取出元素。而在堆中,亲戚亲戚他们他们他们 他们他们他们 有的是按照元素进入队列的先后顺序取出元素的,只是 按照元素的优先级取出元素。

这就好像候机的完后 ,无论谁先到达候机厅,无缘无故头等舱的乘客先登机,我应该 是商务舱的乘客,最后是经济舱的乘客。每个乘客有的是头等舱、商务舱、经济舱有一种 个键值(key)中的五个 。头等舱->商务舱->经济舱依次享有从高到低的优先级。

再比如,封建社会的等级制度,也是五个 堆。在有一种 堆中,国王、贵族、骑士和农民是从高到低的优先级。

封建等级

Linux内核中的调度器(scheduler)会按照各个应用应用程序运行的优先级来安排CPU执行哪五个 应用应用程序运行。计算机中通常有多个应用应用程序运行,每个应用应用程序运行有不同的优先级(该优先级的计算会综合多个因素,比如应用应用程序运行所时要耗费的时间,应用应用程序运行可能等待英文的时间,用户的优先级,用户设定的应用应用程序运行优先程度等等)。内核会找到优先级最高的应用应用程序运行,并执行。可能有优先级更高的应用应用程序运行被提交,可不可否了 调度器会转而安排该应用应用程序运行运行。优先级比较低的应用应用程序运行则会等待英文。“堆”是实现调度器的理想数据行态。

(Linux中可不可否使用nice命令来影响应用应用程序运行的优先级)

 

堆的实现

堆的五个 经典的实现是完整二叉树(complete binary tree)。另五个 实现的堆成为二叉堆(binary heap)

完整二叉树是增加了限定条件的二叉树。假设五个 二叉树的深层为n。为了满足完整二叉树的要求,该二叉树的前n-1层时要填满,第n层也时要按照从左到右的顺序被填满,比如下图:

为了实现堆的操作,亲戚亲戚他们他们他们 他们他们他们 额外增加五个 要求: 任意节点的优先级不小于它的子节点。可能在上图中,设定小的元素值享有高的优先级,可不可否了 上图就符合该要求。

同类事于“叠罗汉”。叠罗汉最重要的许多,只是 让体重大的参与者站在最下面,让体重小的参与者站在后面 (体重小,优先级高)。为了让“堆”稳固,亲戚亲戚他们他们他们 他们他们他们 每次只允许最后面 的参与者退出堆。也只是 ,每次取出的优先级最高的元素。

五个 “叠罗汉”堆

我可能在排序算法简介及其C实现中实际使用了堆。堆的主要操作是插入删除最小元素(元素值有一种 为优先级键值,小元素享有高优先级)。在插入可能删除操作完后 ,亲戚亲戚他们他们他们 他们他们他们 时要保持该实现应有的性质: 1. 完整二叉树 2. 每个节点值都小于或等于它的子节点。

插入操作的完后 ,会破坏上述堆的性质,只是时要进行名为percolate_up的操作,以进行恢复。新插入的节点new倒进完整二叉树最后的位置,再和父节点比较。可能new节点比父节点小,可不可否了 交换两者。交换完后 ,继续和新的父节点比较…… 直到new节点不比父节点小,可能new节点成为根节点。另五个 得到的树,就恢复了堆的性质。

亲戚亲戚他们他们他们 他们他们他们 插入节点2:

插入

删除操作可不可否了删除根节点。根节点删除后,亲戚亲戚他们他们他们 他们他们他们 会有五个 子树,亲戚亲戚他们他们他们 他们他们他们 时要基于它们重构堆。进行percolate_down的操作: 让最后五个 节点last成为新的节点,从而构成五个 新的二叉树。再将last节点不断的和子节点比较。可能last节点比五个 子节点中小的那五个 大,则和该子节点交换。直到last节点不大于任一子节点都小,可能last节点成为叶节点。

删除根节点1。如图:

删除根节点

下面是代码。与亲戚亲戚他们他们他们 他们他们他们 在二叉搜索树中使用表不同,亲戚亲戚他们他们他们 他们他们他们 这里使用数组来表示完整二叉树。数组下标为0的元素不必于储存节点,而用于记录完整二叉树中元素的总数。

/* By Vamei 
   Use an big array to implement heap
   DECLARE: int heap[MAXSIZE] in calling function
   heap[0] : total nodes in the heap
   for a node i, its children are i*2 and i*2+1 (if exists)
   its parent is i/2  */

void insert(int new, int heap[]) 
{
    int childIdx, parentIdx;
    heap[0] = heap[0] + 1;
    heap[heap[0]] = new;
    
    /* recover heap property */
    percolate_up(heap);
}

static void percolate_up(int heap[]) {
    int lightIdx, parentIdx;
    lightIdx  = heap[0];
    parentIdx = lightIdx/2;
    /* lightIdx is root? && swap? */
    while((parentIdx > 0) && (heap[lightIdx] < heap[parentIdx])) {
        /* swap */
        swap(heap + lightIdx, heap + parentIdx); 
        lightIdx  = parentIdx;
        parentIdx = lightIdx/2;
    }
}


int delete_min(int heap[]) 
{
    int min;
    if (heap[0] < 1) {
        /* delete element from an empty heap */
        printf("Error: delete_min from an empty heap.");
        exit(1);
    }

    /* delete root 
       move the last leaf to the root */
    min = heap[1];
    swap(heap + 1, heap + heap[0]);
    heap[0] -= 1;

    /* recover heap property */
    percolate_down(heap);
 
    return min;
}

static void percolate_down(int heap[]) {
    int heavyIdx;
    int childIdx1, childIdx2, minIdx;
    int sign; /* state variable, 1: swap; 0: no swap */

    heavyIdx = 1;
    do {
        sign     = 0;
        childIdx1 = heavyIdx*2;
        childIdx2 = childIdx1 + 1;
        if (childIdx1 > heap[0]) {
            /* both children are null */
            break; 
        }
        else if (childIdx2 > heap[0]) {
            /* right children is null */
            minIdx = childIdx1;
        }
        else {
            minIdx = (heap[childIdx1] < heap[childIdx2]) ?
                          childIdx1 : childIdx2;
        }

        if (heap[heavyIdx] > heap[minIdx]) {
            /* swap with child */
            swap(heap + heavyIdx, heap + minIdx);
            heavyIdx = minIdx;
            sign = 1;
        }
    } while(sign == 1);
}

让人尝试一下构建另一方的main函数,测试相关的操作。

总结

堆,优先级

插入元素,删除最大优先级元素

欢迎继续阅读“纸上谈兵: 算法与数据行态”系列。