Java数据结构中堆的向下和向上调整解析

目录
  • 一、关于堆
    • 1.堆的概念
    • 2.堆的性质
    • 3.堆的存储方式
  • 二、堆的创建
    • 1.堆向下调整
    • 2.堆的创建
  • 三、向上调整

一、关于堆

JDK1.8中的PriortyQueue(优先级队列)底层使用了堆的数据结构,而堆实际就是在完全二叉树的基础之上进行了一些元素的调整。

1.堆的概念

堆有最大堆和最小堆之分。
最大(最小)堆是一棵每一个节点的元素都不小于(大于)其孩子(如果存在)的元素的树。大堆是一棵完全二叉树,同时也是一棵最大树。小堆是一棵完全二叉树,同时也是一棵最小树。
注意: 堆中的任一子树也是堆,即大堆的子树也都是大堆,小堆亦是。

2.堆的性质

堆中某个结点的值总是不大于或不小于其父结点的值
堆总是一颗完全二叉树

3.堆的存储方式

由堆的概念可知,堆是一颗完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。
注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要能够存储空结点,就会导致空间利用率比较低

二、堆的创建

1.堆向下调整

对于给出的一个数据,如何将其创建为堆呢?例如下图:

仔细观察上图后发现:根结点的左右子树已经完全满足堆的性质,因此只需将根结点向下调整好即可。
以小堆为例:

1.让parent标记需要调整的结点,child标记parent的左孩子(注意:parent如果有孩子一定是先有左孩子)
2.如果parent的左孩子存在,即child<size,进行如下操作,直到parent的左孩子不存在

parent右孩子是否存在,如果存在则找出左右孩子中较小的孩子,使用child进行标记
将parent与较小的孩子(也就是此时的child)比较,如果:

parent小于较小的孩子child,这个结点已经调整
否则:将parent与child进行交换,交换成功后,这时parent中大的元素已经向下移动,可能会导致子树不满足堆的特性,就需要继续向下调整,即parent=child,child=parent*2+1,然后循环起来

图解如下:

代码实现:

    private void shiftDown(int parent){
        //默认让child先标记左孩子---因为:parent可能有左没有右
        int child=parent*2+1;

        //while循环条件可以保证:parent的左孩子一定存在
        //             但是不能保证parent的右孩子是否存在
        while(child<size){
            //1.找到左右孩子中较小的孩子
            if(child+1<size&&array[child+1]<array[child]){
                child+=1;
            }

            //2.较小的孩子已经找到了
            //检测双亲和孩子之间是否满足堆的特性
            if(array[parent]>array[child]){
                swap(parent,child);

                //大的双亲往下走,可能会导致子树又不满足堆的特性
                //因此需要继续往下调整
                parent=child;
                child=parent*2+1;
            }else{
                //以parent为根的二叉树已经是堆了
                return;
            }
        }
    }

注意: 在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整。
时间复杂度(看最坏的情况): 从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为O(logn)。

2.堆的创建

向下调整的情况只能针对左右子树已经是堆了才可以调整,那假如根结点的左右子树不满足堆的特性,又该如何调整呢?例如下图:

我们要从3这里的位置开始向下调整,然后逐渐向前依次向上调整
3这个位置很特殊,他是二叉树倒数第一个非叶子结点
步骤:

1.找到倒数第一个非叶子结点
2.从该结点位置开始往前一直到根结点,每遇到一个结点就使用向下调整

代码实现:

public static void createHeap(int[] array){
    //注意:倒数第一个非叶子节点刚好是最后一个节点的双亲
    //最后一个结点的编号是size-1,倒数第一个非叶子节点的下标为(size-1-1)/2
    int lastLeafParent=(size-2)/2;
    //从倒数第一个非叶子节点位置开始,一直到根节点的位置,使用向下调整
    for(int root=lastLeafParent;root>=0;root--){
       shiftDown(root);
    }
}

建堆的时间复杂度:
因为堆是完全二叉树,满二叉树也是完全二叉树,为了简化计算,此处使用满二叉树来证明:
假设满二叉树高度h

第一层:20个结点,需要向下移动h-1层
第二层:21个结点,需要向下移动h-2层
第二层:22个结点,需要向下移动h-3层
…以此类推就可以求出所有的移动步数:每一层结点数与对应移动层数相乘再整体相加
然后再利用一定的数学巧妙运算(此处省略那些繁琐的数学公式,属实是头大)就得出T(n)=n=log(n+1)≈n

因此:建堆的时间复杂度为O(N)。

三、向上调整

向上调整主要的应用场景就是在堆的插入
堆的插入总共需要两个步骤:

1.先将元素插入到堆的末尾,即最后一个孩子之后
2.插入后如果堆的性质遭到破坏,将最后新插入的节点向上调整,直到满足堆的性质

代码实现:

    private void shiftUp(int child){
        int parent=(child-1)/2;

        while(child!=0){
            if(array[child]<array[parent]){
                swap(child,parent);
                child=parent;
                parent=(child-1)/2;
            }else{
                return;
            }
        }
    }

到此这篇关于Java数据结构中堆的向下和向上调整解析的文章就介绍到这了,更多相关Java 数据结构 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java实现堆排序和图解

    目录 堆排序基本介绍 堆排序基本思想 堆排序图解 步骤一 步骤二 代码实现 总结 堆排序基本介绍 1.堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序. 2.堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系. 3.每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆 4.大顶堆举例说明 大顶堆特点:arr[i]

  • java数据结构-堆实现优先队列

    目录 一.二叉树的顺序存储 1.堆的存储方式 2.下标关系 二.堆(heap) 1.概念 2.大/小 根堆 2.1小根堆 2.2大根堆 3.建堆操作 3.1向下调整 4.入队操作 4.1向上调整 4.2push 入队的完整代码展示 5.出队操作 5.1pop 出队代码完全展示 6.查看堆顶元素 7.TOK 问题 7.1TOPK 8.堆排序 文章内容介绍大纲 一.二叉树的顺序存储 1.堆的存储方式 使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中. 一般只适合表示完全二叉树,因为非完

  • Java 数据结构之堆的概念与应用

    目录 什么是堆 堆的类型 小根堆 大根堆 堆的基本操作:创建堆 堆的时间复杂度和空间复杂度 堆的应用-优先级队列 概念 优先级队列基本操作 入优先级队列 出优先级队列首元素 java的优先级队列 堆的常见面试题 最后一块石头的重量 找到K个最接近的元素 查找和最小的K对数字 java数据结构的堆 什么是堆 堆指的是使用数组保存完全二叉树结构,以层次遍历的方式放入数组中. 如图: 注意:堆方式适合于完全二叉树,对于非完全二叉树若使用堆则会造成空间的浪费 对于根节点与其左右孩子在数组中的下标关系可表

  • Java数组与堆栈相关知识总结

    一.数组创建 1.1 声明并赋值 int[] a = {1,2,3}; 1.2 声明数组名开辟空间并且赋值 int[] a; a = new int[]{1,2,3}; 1.3 声明数组时指定元素个数然后赋值 int[] a= new int[3]; 这里Java会默认数组元素值为0 1.4 在以上的基础上创建多维数组 int[][] a = {{1,2,3},{4,5,6},{7,8,9}}; //每个子数组元素个数不要求均相同 int[][] a = new int[m][n]; //其中n

  • java 数据结构之堆排序(HeapSort)详解及实例

    1 堆排序 堆是一种重要的数据结构,分为大根堆和小根堆,是完全二叉树, 底层如果用数组存储数据的话,假设某个元素为序号为i(Java数组从0开始,i为0到n-1),如果它有左子树,那么左子树的位置是2i+1,如果有右子树,右子树的位置是2i+2,如果有父节点,父节点的位置是(n-1)/2取整.最大堆的任意子树根节点不小于任意子结点,最小堆的根节点不大于任意子结点. 所谓堆排序就是利用堆这种数据结构的性质来对数组进行排序,在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的性质可知,最大的

  • Java数据结构中堆的向下和向上调整解析

    目录 一.关于堆 1.堆的概念 2.堆的性质 3.堆的存储方式 二.堆的创建 1.堆向下调整 2.堆的创建 三.向上调整 一.关于堆 JDK1.8中的PriortyQueue(优先级队列)底层使用了堆的数据结构,而堆实际就是在完全二叉树的基础之上进行了一些元素的调整. 1.堆的概念 堆有最大堆和最小堆之分. 最大(最小)堆是一棵每一个节点的元素都不小于(大于)其孩子(如果存在)的元素的树.大堆是一棵完全二叉树,同时也是一棵最大树.小堆是一棵完全二叉树,同时也是一棵最小树. 注意: 堆中的任一子树

  • java 数据结构中栈和队列的实例详解

    java 数据结构中栈和队列的实例详解 栈和队列是两种重要的线性数据结构,都是在一个特定的范围的存储单元中的存储数据.与线性表相比,它们的插入和删除操作收到更多的约束和限定,又被称为限定性的线性表结构.栈是先进后出FILO,队列是先进先出FIFO,但是有的数据结构按照一定的条件排队数据的队列,这时候的队列属于特殊队列,不一定按照上面的原则. 实现栈:采用数组和链表两种方法来实现栈 链表方法: package com.cl.content01; /* * 使用链表来实现栈 */ public cl

  • java 数据结构中栈结构应用的两个实例

    java 数据结构中栈结构应用的两个实例 1.单词逆序. 要求从控制台读入一串字符,按回车结束输入,同时显示其逆序字符串. 对于颠倒顺序的操作,用栈来解决是很方便的.具体思想是把字符串中的每一个字符按顺序存入栈中,然后再一个一个的从栈中取出.这时就是按照逆序取出的字符串. // reverse.java // stack used to reverse a string // to run this program: C>java ReverseApp import java.io.*; //

  • Java 数据结构中二叉树前中后序遍历非递归的具体实现详解

    目录 一.前序遍历 1.题目描述 2.输入输出示例 3.解题思路 4.代码实现 二.中序遍历 1.题目描述 2.输入输出示例 3.解题思路 4.代码实现 三.后序遍历 1.题目描述 2.输入输出示例 3.解题思路 4.代码实现 一.前序遍历 1.题目描述 给你二叉树的根节点 root ,返回它节点值的 前序 遍历. 2.输入输出示例 示例 1: 输入:root = [1,null,2,3] 输出:[1,2,3] 示例2: 输入:root = [] 输出:[] 示例 3: 输入:root = [1

  • Java数据结构中图的进阶详解

    目录 有向图 有向图API设计 有向图的实现 拓扑排序 拓扑排序图解 检测有向图中的环 检测有向环的API设计 检测有向环实现 代码 基于深度优先的顶点排序 顶点排序API设计 顶点排序实现 代码: 有向图 有向图的定义及相关术语 定义∶ 有向图是一副具有方向性的图,是由一组顶点和一组有方向的边组成的,每条方向的边都连着 一对有序的顶点. 出度∶ 由某个顶点指出的边的个数称为该顶点的出度. 入度: 指向某个顶点的边的个数称为该顶点的入度. 有向路径︰ 由一系列顶点组成,对于其中的每个顶点都存在一

  • Java 超详细讲解数据结构中的堆的应用

    目录 一.堆的创建 1.向下调整(以小堆为例) 2.创建堆 3.创建堆的时间复杂度 二.堆的插入和删除 1.堆的插入 2.堆的删除 三.堆的应用 1.堆排序 2.top-k问题 [求最小的K个数] 四.常用接口的介绍 1.PriorityQueue的特性 2.优先级队列的构造 一.堆的创建 1.向下调整(以小堆为例) 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子) 如果parent的左孩子存在,即:child < size,

  • Java 超详细讲解数据结构中的堆的应用

    目录 一.堆的创建 1.向下调整(以小堆为例) 2.创建堆 3.创建堆的时间复杂度 二.堆的插入和删除 1.堆的插入 2.堆的删除 三.堆的应用 1.堆排序 2.top-k问题(求最小的K个数) 四.常用接口的介绍 1.PriorityQueue的特性 2.优先级队列的构造 一.堆的创建 1.向下调整(以小堆为例) 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子) 如果parent的左孩子存在,即:child < size, 进

  • Java深入了解数据结构中常见的排序算法

    目录 一,概念 1,排序 2,稳定性 二,排序详解 1,插入排序 ①直接插入排序 2,选择排序 ①直接选择排序 ②堆排序 3,交换排序 ①冒泡排序 ②快速排序 4,归并排序 一,概念 1,排序 排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作. 平时的上下文中,如果提到排序,通常指的是排升序(非降序). 通常意义上的排序,都是指的原地排序(in place sort). 2,稳定性 两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算

  • 浅析Java类和数据结构中常用的方法

    1.Object类里面常用的方法: protected Object clone()创建并返回此对象的一个副本. boolean equals(Object obj)指示其他某个对象是否与此对象"相等". protected void finalize()当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法. Class<?> getClass()返回此 Object 的运行时类. int hashCode()返回该对象的哈希码值. void notif

  • C语言数据结构中堆排序的分析总结

    目录 一.本章重点 二.堆 2.1堆的介绍(三点) 2.2向上调整 2.3向下调整 2.4建堆(两种方式) 三.堆排序 一.本章重点 堆 向上调整 向下调整 堆排序 二.堆 2.1堆的介绍(三点) 1.物理结构是数组 2.逻辑结构是完全二叉树 3.大堆:所有的父亲节点都大于等于孩子节点,小堆:所有的父亲节点都小于等于孩子节点. 2.2向上调整 概念:有一个小/大堆,在数组最后插入一个元素,通过向上调整,使得该堆还是小/大堆. 使用条件:数组前n-1个元素构成一个堆. 以大堆为例: 逻辑实现: 将

随机推荐