• 二叉堆(binary heap)


     堆(heap) 亦被称为:优先队列(priority queue),是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。在队列中,调度程序反复提取队列中第一个作业并运行,因而实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权。堆即为解决此类问题设计的一种数据结构。

    本文地址:http://www.cnblogs.com/archimedes/p/binary-heap.html,转载请注明源地址。

    逻辑定义

    n个元素序列{k1,k2...ki...kn},当且仅当满足下列关系时称之为堆:
    (ki <= k2i, ki <= k2i+1)或者(ki >= k2i, ki >= k2i+1),   (i = 1,2,3,4...n/2)

    二叉堆一般用数组来表示。如果根节点在数组中的位置是1,第n个位置的子节点分别在2n和 2n+1。因此,第1个位置的子节点在2和3,第2个位置的子节点在4和5。以此类推。这种基于1的数组存储方式便于寻找父节点和子节点。

    如果存储数组的下标基于0,那么下标为i的节点的子节点是2i + 1与2i + 2;其父节点的下标是⌊(i − 1) ∕ 2⌋。

    如下图的两个堆:

            1               11
          /              /   
         2     3         9     10
        /    /        /     / 
       4   5  6  7     5   6  7   8
      /  /          /  / 
     8  9 10 11      1  2 3  4 
    

    将这两个堆保存在以1开始的数组中:

    位置:  1  2  3  4  5  6  7  8  9 10 11
    左图:  1  2  3  4  5  6  7  8  9 10 11
    右图: 11  9 10  5  6  7  8  1  2  3  4

    性质

    二叉堆是完全二叉树或者是近似完全二叉树。

    二叉堆满足二个特性:

    1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

    2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

    当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆

                

                      最小堆                                                           最大堆

    堆支持以下的基本操作:

    • build:建立一个空堆;
    • insert:向堆中插入一个新元素;
    • update:将新元素提升使其符合堆的性质;
    • get:获取当前堆顶元素的值;
    • delete:删除堆顶元素;
    • heapify:使删除堆顶元素的堆再次成为堆。

    某些堆实现还支持其他的一些操作,如斐波那契堆支持检查一个堆中是否存在某个元素。

    基本操作实现:

    1、删除优先级最高的元素(DeleteMin)

    此操作分3步:

    (1)直接删除根

    (2)用最后一个元素代替根

    (3)将堆向下重新调整

      输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直到是叶子结点或其关键字值小于等于左、右子树的关键字的值,将得到新的堆。称这个从堆顶至叶子的调整过程为“筛选”

    调整堆算法实现如下:

    void down(int p)  /* 调整堆算法 */
    {
        int q = p * 2;   /* 向下调整算法,p代表当前结点,q代表子结点 */
        a = heap[p];    /* 保存当前结点的值 */
        while(q <= hlength) {    /* hlength为堆中元素的个数 */
            /* 选择两个子节点中的一个最小的 */
            if(q < hlength && heap[q] > heap[q + 1])
                q++;
            if(heap[q] >= a) {    /* 如果当前结点比子节点小,就结束*/
                break;
            } else {             /* 否则就交换 */
                heap[p] = heap[q];
                p = q;
                q = p * 2;
            }
        }
        heap[p] = a;    /* 安排原来的结点 */
    }

    删除最小元素,返回该最小元素:

    int DeleteMin()
    {
        /* 删除最小元素算法 */
        int r = heap[1];   /* 取出最小元素 */
        heap[1] = heap[hlength--];  /* 把最后一个叶子结点赋值给根结点 */
        down(1);    /* 调用向下调整算法 */
        return r;
    }

    2、在堆中插入一个新元素Insert(x):

    向上调整算法:

    void up(int p)
    {
        /* 向上调整算法,p代表当前结点,q代表父结点 */
        int q = p / 2;  /* 获取当前结点的父结点 */
        a = heap[p];    /* 保存当前结点的值 */
        while(q >= 1 && a < heap[q]) {
            heap[p] = heap[q];  /* 如果当前结点的值比父母结点的值小,就交换 */
            p = q;
            q = p / 2;
        }
        heap[p] = a;   /* 安排原来的结点 */
    }

    插入元素算法:

    void insert(int a)
    {
        heap[++hlength] = a;  /* 先往堆里插入结点值 */
        up(hlength);  /* 向上调整 */
    }

    3、将x的优先级上升为p:

    void IncreaseKey(int a, int p)  /* 把p的优先级升为a */
    {
        if(heap[p] < a)
            return;
        heap[p] = a;
        up(p);
    }

    4、建立堆:

    void bulid()  /* 建堆算法 */
    {
        int i;
        for(i = hlength / 2; i > 0; i--)  
            down(i);   /* 从最后一个非终端结点开始进行调整 */
    }

    编程实践

     poj2051 Argus

     http://poj.org/problem?id=2051

    #include<stdio.h>
    #include<string.h>
    char str[10];
    typedef struct Node {
        int now, Q, P;
    }Node;
    Node node[3001];
    void down(Node H[], int s, int m)
    {
        int j;
        Node rc = H[s];
        for(j = s * 2; j <= m; j *= 2) {
            if(j < m) {
                if(H[j].now > H[j + 1].now) {
                    j++;
                } else {
                    if((H[j].now == H[j + 1].now) && (H[j].Q > H[j + 1].Q))
                        j++;
                }
            }
            if(rc.now < H[j].now || (rc.now == H[j].now && rc.Q < H[j].Q))
                break;
            H[s] = H[j];
            s = j;
        }
        H[s] = rc;
    }
    
    void BulidMinHeap(Node H[], int length)
    {
        int i;
        for(i = length / 2; i > 0; i--)
            down(H, i, length);
    }
    
    void solve()
    {
        int i, len, K;
        i = 1;
        while(scanf("%s", str) && str[0] == 'R') {
            scanf("%d %d", &node[i].Q, &node[i].P);
            getchar();
            node[i].now = node[i].P;
            i++;
        }
        len = i - 1;
        scanf("%d", &K);
        BulidMinHeap(node, len);
        for(i = 1; i <= K; i++) {
            printf("%d
    ", node[1].Q);
            node[1].now += node[1].P;
            down(node, 1, len);
        }
    }
    int main()
    {
        solve();
        return 0;
    }

    参考资料

    1、Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein(潘金贵等译)《算法导论》. 机械工业出版社.

    2、Vuillemin, J. (1978). A data structure for manipulating priority queues.Communications of the ACM21, 309–314.

    3、ACM/ICPC 算法训练教程

    4、《数据结构》严蔚敏、吴伟民

    5、维基百科

  • 相关阅读:
    Windos10开机自动 全屏启动 浏览器中的某个 具体网页
    Idea 2019-3.4版本激活码
    CentOS 7安装 MySQL 8.0+ 版本数据库
    CentOS 7更换镜像源
    Linux中 终端、命令、shell解析器 的简单介绍
    angular package.json中start build
    opencv 高级拼接函数Stitcher
    py_faster_rcnn识别出来的结果好多红框重叠
    机器视觉笔记-2
    js获取摄像头视频流
  • 原文地址:https://www.cnblogs.com/wuyudong/p/binary-heap.html
Copyright © 2020-2023  润新知