• 数据结构-二叉堆


    表示形式

    堆是一个数组,可以被看成是一个近似的完全二叉树。表示堆的数组A包含两个属性:A.length表示元素个数,A.heap-size表示有多少个堆元素存储在数组中。

    也就是说,虽然A[1...A.length]可能都存有数据,但是只有A[1...A.heap-size]中存放的是堆的有效元素(0 <= A.heap-size <= A.length)。

    数组元素下标从0开始,可以使用宏来定义左右子节点和父节点:

    #define LEFT(idx) (((idx)<<1)+1)
    #define RIGHT(idx) (((idx)<<1)+2)
    #define PARENT(idx) (((idx)-1)>>1)

     

    这种表示的是二叉堆。

    二叉堆可以分两种形式:最大堆、最小堆。

    其中,最大堆指:除了根节点外,所有节点 i 都要满足 A[PARENT(i)] >= A[i],堆中最大元素在根节点,并且,任一子树中,该子树包含的所有节点的值,都不大于该子树根节点的值。

    最小堆相反,指:除根节点外,所有节点 i 都要满足 A[PARENT(i)] <= A[i],堆中最小元素在根节点,并且,任一子树中,该子树包含的所有节点的值,都不小于该子树根节点的值。

    维护堆的性质

    堆的操作分为“下沉”和“上浮”

    以最大堆为例,下沉方法是:假设某个节点的左右子树都是最大堆了,拿该节点的值和左右子树值对比,取最大的一个(比如是左节点)和该节点交换,这样,这个节点的值就是这个树中最大的了。但是,这样可能破坏左子树的堆性质,于是,继续对左子树根节点执行“下沉”操作。

    “上浮”方法是:假设下标小于该节点的元素,已经构成了最大堆,调节此元素时,与父节点比较,如果大于父节点的值,交换,这样父节点大于此节点的值了。但是,这样可能破坏了祖先节点的堆性质,所以,继续对祖先节点执行“上浮”操作

    // 下沉
    void sink(int* arr, int index, int heapsize)
    {
        // 左子树节点超出堆长度范围
        if (LEFT(index) > heapsize-1)
        {
            return;
        }
    
        int maxidx = -1;
        // 右子树节点超出堆长度范围,或者左节点的值比右节点的值大
        if (RIGHT(index) > heapsize-1 || (arr[LEFT(index)] > arr[RIGHT(index)]))
        {
            maxidx = LEFT(index);
        }
        else
        {
            maxidx = RIGHT(index);
        }
    
        if (maxidx != -1 && arr[maxidx] > arr[index])
        {
            int tmp = arr[maxidx];
            arr[maxidx] = arr[index];
            arr[index] = tmp;
            sink(arr, maxidx, heapsize);
        }
    }
    
    // 上浮
    void upward(int* arr, int index, int heapsize)
    {
        if (0 == index)
        {
            return;
        }
    
        if (arr[PARENT(index)] < arr[index])
        {
            int tmp = arr[index];
            arr[index] = arr[PARENT(index)];
            arr[PARENT(index)] = tmp;
            upward(arr, PARENT(index), heapsize);
        }
    }

     

    建堆

    把一个普通数组调整成为一个堆的过程。有了下沉和上浮的方法,我们可以对每个元素执行这样的操作,实现建堆。

    void adjust_by_sink(int* arr, int len)
    {
        // 下沉法,从最后一个非叶子节点开始,到根节点,依次执行下沉
        for (int i = PARENT(len-1); i >= 0; i--)
        {
            sink(arr, i, len);
        }
    }
    
    void adjust_by_upward(int* arr, int len)
    {
        // 上浮法,从根节点到最后一个节点,依次执行上浮
        for (int i = 0; i < len; i++)
        {
            upward(arr, i, len);
        }
    }

     

    插入元素

    把元素插入最后一个位置,然后,让这个节点上浮,调整为一个堆

    int insert(int* arr, int value, int& heapsize)
    {
        if (heapsize > ARRLEN)
        {
            return -1;
        }
    
        arr[heapsize] = value;
        upward(arr, heapsize, heapsize+1);
        heapsize++;
    
        return 0;
    }

     

    删除元素

    堆的删除元素,一般指删除第一个元素,因为第一个是需要的最大值或者最小值。

    通过把最后一个值赋值给根节点,然后,把根节点下沉,再次调整为一个新的堆

    void del(int* arr, int& heapsize)
    {
        if (heapsize <= 0)
        {
            return;
        }
    
        arr[0] = arr[heapsize-1];
        heapsize--;
        sink(arr, 0, heapsize);
    }

     

     

  • 相关阅读:
    AOP 和 前置通知,后置通知
    使用Spring 简化MyBatis
    核心一:DI
    环境搭建及创建对象方式及赋值(注入)
    核心一:IoC
    Spring框架简介
    判断两个矩形是否相交的4个方法
    计算旋转角度
    浅析adb命令
    如何选择开源许可证?
  • 原文地址:https://www.cnblogs.com/zuofaqi/p/9665619.html
Copyright © 2020-2023  润新知