其他分享
首页 > 其他分享> > 数据结构-二叉堆(大顶堆&小顶堆)

数据结构-二叉堆(大顶堆&小顶堆)

作者:互联网

 

文章目录

 

简介

二叉堆就是一颗二叉树,是一颗完全二叉树,最直观表现一个二叉树左边最多比右边深 1 层,二叉堆我们常常讨论的就是大顶堆和小顶堆,其中大顶堆根结点最大,左右节点依次递归,小顶堆类似

二叉堆算是一种比较重要的数据结构,实际中我们的堆排序就涉及到二叉堆,它也是优先级队列的基础

所以这里我可以说下二叉堆的性质

Java 实现

逻辑思路

首先声明二叉堆是一颗完全二叉树,完全二叉树是可以存储在一个数组中,因为存储在数组中的完全二叉树找到其左右孩子结点甚至双亲节点很容易,这得要通过下标去寻找

由于二叉堆用数组存储,因此遵循的规律如下:(这里重复写一遍)

// 当前结点下标
int currentIndex;

/* 对应着删除的下沉操作 */
// 当前结点的左孩子结点下标
int leftChildIndex = 2 * currentIndex + 1;
// 当前结点的右孩子结点下标
int rightChildIndex = 2 * currentIndex + 2;

/* 对应着新增的上浮操作 */
// 当前结点双亲结点的下标(当前结点不论是双签的左孩子还是右孩子都是满足的)
int parentsIndex = (currentIndex - 1) / 2;

结构图解

堆排序涉及到构建堆,下图就是构建一个大顶堆,从最后一个非叶子结点开始进行下沉操作,循环到根结点也进行下沉操作,最后结束

a

代码实现

下面是针对于大顶堆的代码

下面代码中,对于插入的结点的代码中,数组已经将结点插入到尾部,代码只需要进行上浮操作

删除结点的代码中,数组已经将指定位置的结点删除,然后将最后一个结点放置于被删除的结点处,然后代码只需要进行下沉操作

// 插入结点(需要将插入到尾部的结点上浮操作,该数组中已经将插入的结点放置于数组尾部)
public void upAdjust(int[] arr) {
    int currentIndex = arr.length - 1;
    int parentsIndex = (currentIndex - 1) / 2;
    // 临时保存插入的结点值
    int tmp = arr[currentIndex];
    // 上浮操作循环
    while (parentsIndex >= 0 && arr[parentsIndex] < tmp) {
        // 单向赋值,无交换
        arr[currentIndex] = arr[parentsIndex];
        currentIndex = parentsIndex;
        parentsIndex = (currentIndex - 1) / 2;
    }
    // 最后将临时储存的结点值赋给 currentIndex 指向的结点值
    arr[currentIndex] = tmp;
}


// 删除结点(需要将指定的结点删除,将尾部结点放在被删除结点处,数组中已放,需要进行下沉操作)
public void downAdjust(int[] arr, int currentIndex) {
    int tmp = arr[currentIndex];
    int childIndex = 2 * currentIndex + 1;
    while (childIndex < arr.length) {
        // 判断是左孩子还是右孩子
        if (childIndex + 1 < arr.length && arr[childIndex] < arr[childIndex+1]) {
            childIndex++;
        }
        // 若发现不满足循环条件即可跳出
        if (tmp >= arr[childIndex]) {
            break;
        }
        // 单向赋值,无交换
        arr[currentIndex] = arr[childIndex];
        currentIndex = childIndex;
        childIndex = 2 * currentIndex + 1;
    }
    arr[currentIndex] = tmp;
}


// 创建二叉堆(创建一个大顶堆,一致有一个完全二叉树的数组)
public void buildHeap(int[] arr) {
    // 实际是 ((arr.length - 1) - 1) / 2
    for (int i = (arr.length - 2) / 2; i >= 0; i++) {
        // 每个非叶子结点都需要做下沉操作,从最后一个开始往前遍历
        downAdjust(arr, i);
    }
}
时间复杂度

满二叉树的结点数是 n,那么其深度应该是 log 以 2 为底 n 的对数

一些疑问
  1. 为什么删除操作不能是删除根结点,然后不把最后一个元素移到头部,直接去做筛选元素上浮呢?

    答:因为如果过这么去做,会发现最终结果可能无法构成一个完全二叉树!打个比方如下:

            1
        3		2
            
    4	5        
    

    现在删除 1,不把 5 移动到头部,那么 2 直接上浮占据了 1 的位置,此时 2 的位置置空,那么左边深度要比右边大 2 了,这也就不是一颗完全二叉树了,自然也不能存储数组里去了!

 

标签:大顶,结点,int,arr,二叉,currentIndex,小顶,二叉树
来源: https://blog.51cto.com/u_13281972/2993769