zoukankan      html  css  js  c++  java
  • 【暖*墟】 #二叉堆# 大根堆的常见操作

    一、二叉堆的定义

    二叉堆使用完全二叉树(其前n-1层必须被填满,第n层也要从左到右顺序填满)来实现。

    在二叉堆中,所有非终端结点的值均不大于(或不小于)其左右孩子的值。

    非终端结点的值均不大于其左右孩子结点的值,这样的二叉堆叫做小根堆(下图b),

    小根堆根结点的值是该堆中所有结点的最小值;

    同样的,若非终端结点的值都不小于其左右孩子的值,这样的堆叫做大根堆(下图a),

    大根堆根结点的值为改堆所有结点的最大值。利用堆的此性质,可以实现堆排序。

    说明:小根堆和大根堆的实现没有太大的区别,所以下面以大根堆为例。

    二、二叉堆的操作

    堆一般使用数组来构建,假设为数组a[],结点通常存储在a[1],

    这样对于下标为k的结点a[k]来说,其左孩子的下标为2*k,右孩子的下标为2*k+1

    1、插入结点到堆中

    结点插入的位置应该是完全二叉树的最后一个位置(如下图所示),

    对于大根堆来讲,需要满足两个性质:

    (1)堆为完全二叉树;(2)堆中每个父结点的值都不小于其左右子结点的值。

    插入结点可能会破坏这两条性质,所以在插入结点后需要对堆进行调整。

    调整方法为:将插入的结点与其父结点比较,若大于其父结点的值,则交换两者。

    重复此操作,直至该结点不比其父结点大,或者该结点成为根结点。

    可以通过插入结点到一个已经存在的堆中,也可以通过不断插入结点来构建一个堆

     

    //insert
    int heap[maxn],n;
    void up(int p){ //通过交换位置p实现向上调整
        while(p>1){ //没有到达根节点
            if(heap[p]>heap[p/2]){
                swap(heap[p],heap[p/2]);
                p/=2; //编号变小:(2n+1)/2=2n/2=n。
            }
            else break; //找到位置,退出循环
        }
    }
    void insert(int val){
        heap[++n]=val; up(n); //放在底端,记录价值
    }

    ↓↓↓GetTop:返回堆顶权值

    //gettop
    int gettop(){
        return heap[1];
    }

    2、删除堆顶元素(堆排序)

    删除堆顶元素(根结点)后,会得到左右两棵子树,此时将堆中最后一个元素移到堆顶

    然后自上而下调整,将该结点与左右孩子结点比较,此时会有三种情况:

    (1)结点的左右孩子均为空,此时调整结束

    (2)结点只有左孩子,将该结点与左比较。若结点小于其左孩子,则两者交换,否则调整结束;

    (3)结点左右孩子都非空,则将该结点与左右较小者比较,若小于则交换,否则调整结束;

    重复此过程,直到该结点不小于其左右孩子结点,或者该结点为叶子结点。

    //extract
    //删除堆顶元素,将末尾元素放到堆顶位置,再调整
    void down(int p){ //向下调整
        int s=p*2; //p的左子节点
        while(s<=n){ //还没成为叶子结点
            if(s<n&&heap[s]<heap[s+1]) s++; //取左右节点较大值
            if(heap[s]>heap[p]){
                swap(heap[s],heap[p]);
                p=s,s=p*2; //现在位置变成s,子节点s相应改变
            }
            else break;
        }
    }
    void extract(){
        heap[1]=heap[n--]; //n处元素放在堆顶,n--
        down(1);
    }

    3、删除p处元素

    //remove(p)
     
    void up(int p){ //通过交换位置,实现向上调整
        while(p>1){ //没有到达根节点
            if(heap[p]>heap[p/2]){
                swap(heap[p],heap[p/2]);
                p/=2; //编号变小:(2n+1)/2=2n/2=n。
            }
            else break; //找到位置,退出循环
        }
    }
     
    void down(int p){ //向下调整
        int s=p*2; //p的左子节点
        while(s<=n){ //还没到末尾
            if(s<n&&heap[s]<heap[s+1]) s++; //取左右节点较大值
            if(heap[s]>heap[p]){
                swap(heap[s],heap[p]);
                p=s,s=p*2; //现在位置变成s,子节点s相应改变
            }
            else break;
        }
    }
     
    void remove(int p){
        heap[p]=heap[n--]; //n处元素放在k处,n--
        up(p); down(p); //可能向上或者向下调整
    }

    三.stl中的优先队列

    priority_queue实现了一个大根堆。

    支持 push(i),top(),pop()操作,不支持 remove 操作。

    priority_queue<int> xxx 大根堆

    priority_queue<int, vector<int>, greater<int>> xxxx 小根堆

    //注意,多重符号时中间要打空格分隔符,否则‘>>’会影响。

                                                       ——时间划过风的轨迹,那个少年,还在等你。

  • 相关阅读:
    使用熔断器防止服务雪崩
    创建服务消费者(Feign)
    1.python进行if条件相等时候的条件
    理解编程语言是什么
    硬件架构与操作系统的历史
    centos7 下安装rpm的mysql 5.7
    BIND的进阶二:视图,日志,转发,子域的授权
    Linux启动盘-ultraiso
    ubuntu 跟xshell的问题
    Python接口自动化-requests模块之get请求
  • 原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/9346754.html
Copyright © 2011-2022 走看看