Java版大顶堆的实现

堆的概念

堆是一棵完全二叉树,一般使用数组来存储。通俗来讲堆其实就是利用数组来维护一个完全二叉树

按照堆的特点可以把堆分为大顶堆和小顶堆

  • 大顶堆:堆的每个结点的值都大于或等于其左右孩子结点的值

  • 小顶堆:堆的每个结点的值都小于或等于其左右孩子结点的值

根据堆的概念(利用数组维护的完全二叉树),可以推导出:
假设 节点A 在数组 tree 的索引为 i

(1)A节点的左节点索引:leftIdx = (i+1)*2 -1
(2)A节点的右节点索引:rightIdx = (i+1)*2
(3)A的父节点索引:parentIdx = (i-1)/2

堆的构建

堆的构建可以看成是空堆中逐渐插入数据,因此构建堆,应该先实现堆的插入方法。

往堆中插入节点

往堆中插入数据时,可能会破坏大顶堆(或小顶堆),根节点大于左右节点的性质,因此需要做出调整。

堆的插入流程如下:

  1. 将插入的数组置于数组的尾部
  2. 从尾部开始比较当前节点(一开始为插入的节点)与父节点是否满足大顶堆(或小顶堆)的性质,不满足则交换父子节点。
  3. 重复2步骤,直到满足大顶堆性质或到达堆顶

示例代码:

	/*** 插入的流程:* 1.先把元素放到数组最后* 2.与父元素比较,若父元素小于子元素则交换父子元素,直到父元素大于等于子元素* @param n*/public void add(int n){if(size>=capacity){throw new RuntimeException("堆达到最大容量");}//新节点的索引int curIdx = size;//将新节点插入数组尾部table[curIdx] = n;//获得父节点索引,具体代码在参考后文的完整示例//父节点索引 pIdx = (curIdx-1)/2int pIdx = getParent(curIdx);/*** 调整堆,使其符合大顶堆的性质,时间复杂度O(logn)* 与父元素比较,若父元素小于子元素则交换父子元素,直到父元素大于等于子元素*/while(table[curIdx]>table[pIdx]){//子节点值大于父节点,交换父子节点int t = table[pIdx];table[pIdx] = table[curIdx];table[curIdx] = t;curIdx = pIdx;pIdx = getParent(curIdx);}size++;}

在清楚堆的插入过程之后,堆的构建就是直接调用插入方法,往堆中存放数据。

同时由上述可知,往堆中添加元素的时间复杂度为 O(logN),因此构建堆的时间复杂度为n次插入,即 O(NlogN)

删除堆顶节点

删除堆顶元素的流程:

  1. 用堆的最后一个元素(数组中的最后一个元素),代替堆顶元素(数组的第一个元素)
  2. 判断当前元素(一开始为堆顶),是否小于左右子元素,小于则用左右子元素中较大的元素和当前元素进行交换
  3. 重复步骤2,直接节点大于左右元素或没有左右元素

示例代码:

/*** 删除并返回堆顶元素* 删除流程:* 1.把最后一个元素代替删除位置的元素* 2.与子元素比较,把较大的子元素与当前元素交换,直到当前元素大于左右子元素* @return 堆顶元素*/public int remove(){//待删除的堆顶元素int v = table[0];//用堆的最后一个元素(数组中的最后一个元素),代替堆顶元素(数组的第一个元素)table[0] = table[size-1];size--;//调正堆,使堆满足大顶堆的性质//当前元素索引,一开始为堆顶元素int curIdx = 0;while(true){//获得当前元素的左右节点索引,具体代码参考后面完整示例int lf = getLeftChild(curIdx);int rt = getRightChild(curIdx);if(lf==-1&&rt==-1){//没有左右元素,无需调整break;}//较大的子元素索引int swapIdx;if(lf==-1||rt==-1){swapIdx = lf==-1?rt:lf;}else{swapIdx = table[rt]>table[lf]?rt:lf;}if(table[curIdx]>table[swapIdx]){//父元素大于左右子元素,结束交换break;}//用较大的子元素代替父元素int t = table[curIdx];table[curIdx] = table[swapIdx];table[swapIdx] = t;curIdx = swapIdx;}//返回被删除的元素return v;}

完整代码

/*** @author Darren* @date 2021/6/24 15:54* 简单大顶堆的实现*/
public class Heap {//堆的容量int capacity;//已存放的节点数int size;//存放完全二叉树结构的数组int[] table;Heap(int capacity){this.capacity = capacity;table = new int[capacity];}/*** 插入的流程:* 1.先把元素放到数组最后* 2.与父元素比较,若父元素小于子元素则交换父子元素,直到父元素大于等于子元素* @param n*/public void add(int n){if(size>=capacity){throw new RuntimeException("堆达到最大容量");}//当前元素的索引int curIdx = size;//将新节点插入数组尾部table[curIdx] = n;int pIdx = getParent(curIdx);/*** 调整堆,使其符合大顶堆的性质,时间复杂度O(logn)* 与父元素比较,若父元素小于子元素则交换父子元素,直到父元素大于等于子元素*/while(table[curIdx]>table[pIdx]){int t = table[pIdx];table[pIdx] = table[curIdx];table[curIdx] = t;curIdx = pIdx;pIdx = getParent(curIdx);}size++;}/*** 删除堆顶元素* 删除流程:* 1.把最后一个元素代替删除位置的元素* 2.与子元素比较,把较大的子元素与当前元素交换,直到当前元素大于左右子元素* @return*/public int remove(){//待删除的堆顶元素int v = table[0];//用堆的最后一个元素(数组中的最后一个元素),代替堆顶元素(数组的第一个元素)table[0] = table[size-1];size--;//调正堆,使堆满足大顶堆的性质//当前元素索引,一开始为堆顶元素int curIdx = 0;while(true){int lf = getLeftChild(curIdx);int rt = getRightChild(curIdx);if(lf==-1&&rt==-1){//没有左右元素,无需调整break;}//较大的子元素索引int swapIdx;if(lf==-1||rt==-1){swapIdx = lf==-1?rt:lf;}else{swapIdx = table[rt]>table[lf]?rt:lf;}if(table[curIdx]>table[swapIdx]){//父元素大于左右子元素,结束交换break;}//用较大的子元素代替父元素int t = table[curIdx];table[curIdx] = table[swapIdx];table[swapIdx] = t;curIdx = swapIdx;}return v;}public int getLeftChild(int i){int index = (i+1)*2-1;//index>=size 说明没有左子节点return index<size?index:-1;}public int getRightChild(int i){int index = (i+1)*2;//index>=size 说明没有右子节点return index<size?index:-1;}public int getParent(int i){return (i-1)/2;}
}

参考

  • 推荐:堆排序(大顶堆、小顶堆)----C语言

  • 排序六 堆排序

  • 数据结构:堆(Heap)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330072.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 8新特性探究(二)深入解析默认方法

转载自 Java 8新特性探究&#xff08;二&#xff09;深入解析默认方法 什么是默认方法&#xff0c;为什么要有默认方法 简单说&#xff0c;就是接口可以有实现方法&#xff0c;而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。 为什么要有这个特性&am…

把本地库推送到github远程库

【1】 github上创建远程库 注意 &#xff0c;远程库的名字要与本地库相同 【2】新建github远程库别名origin 【3】 代码提交 git add ./* &#xff1a; 把修改内容添加到暂存区 &#xff1b; git commit -m msg &#xff1a; 提交暂存区的修改内容到本地库&#xff1b; g…

react antd confirm content list_React造轮系列:对话框组件 - Dialog 思路

React造轮系列&#xff1a;对话框组件 - Dialog 思路对话框一般是我们点击按钮弹出的这么一个东西&#xff0c;主要类型有 Alter, Confirm 及 Modal, Modal 一般带有半透明的黑色背景。当然外观可参考 AntD 或者 Framework 等。确定 APIAPI 方面主要还是要参考同行&#xff0c;…

Spring IOC 和 AOP 概览

IOC&#xff08;控制反转&#xff09; IoC&#xff08;Inversion of Control&#xff0c;控制倒转&#xff09;。所谓IoC&#xff0c;对于spring框架来说&#xff0c;就是由spring来负责控制对象的生命周期和对象间的关系。 在没有IOC时&#xff0c;我们通过new 等关键字等方…

Java 并发实践 — ConcurrentHashMap 与 CAS

转载自 Java 并发实践 — ConcurrentHashMap 与 CAS最近在做接口限流时涉及到了一个有意思问题&#xff0c;牵扯出了关于concurrentHashMap的一些用法&#xff0c;以及CAS的一些概念。限流算法很多&#xff0c;我主要就以最简单的计数器法来做引。先抽象化一下需求&#xff1a;…

git rebase命令(转)

转自&#xff1a; https://www.yiibai.com/git/git_rebase.html git rebase命令在另一个分支基础之上重新应用&#xff0c;用于把一个分支的修改合并到当前分支。 使用语法 git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>][<u…

python tkinter计算器实例_python -Tkinter 实现一个小计算器功能

原博文 2017-03-25 22:08 − 文章来源&#xff1a;http://www.cnblogs.com/Skyyj/p/6618739.html 本代码是基于python 2.7的 如果是对于python3.X 则需要将 tkinter 改为Tkinter 将tkMessagebox&... 相关推荐 2019-12-10 15:59 − python GUI编程(Tkinter) Python 提供了多…

Spring IOC 容器启动、Bean生命周期详解

前言 在Spring IOC 和 AOP 概览中&#xff0c;简要介绍了IOC容器和AOP&#xff0c;没有深入IOC容器Bean的实例化&#xff0c;此文承接上文深入分析Bean的实例化过程、生命周期。 Spring IOC的过程 Spring的IoC容器在实现控制反转和依赖注入的过程中,可以划分为两个阶段: 容…

java 8 Lambda 表达式(副作用)

【1】转自&#xff1a; https://www.cnblogs.com/linlinismine/p/9283532.html 早在2014年oracle发布了jdk 8,在里面增加了lambda模块。于是java程序员们又多了一种新的编程方式&#xff1a;函数式编程&#xff0c;也就是lambda表达式。我自己用lambda表达式也差不多快4年了&am…

Java NIO:浅析I/O模型

转载自 Java NIO&#xff1a;浅析I/O模型也许很多朋友在学习NIO的时候都会感觉有点吃力&#xff0c;对里面的很多概念都感觉不是那么明朗。在进入Java NIO编程之前&#xff0c;我们今天先来讨论一些比较基础的知识&#xff1a;I/O模型。下面本文先从同步和异步的概念 说起&…

ubuntu安装python3.8_Ubuntu 16.04 安装 python3.8

Ubuntu 16.04 amd64 (64bit)&#xff08;纯净版&#xff09; 自带python2.7和python3.5 执行"whereis python"查看当前安装的python [rootroot ~]# whereis python python: /usr/bin/python2.7 /usr/bin/python /usr/lib/python2.7 /usr/lib64/python2.7 /etc/python…

Spring IOC 如何解决循环依赖?

前言 假设对象A、B 之间相互依赖&#xff0c;Spring IOC是如何解决A、B两个对象的实例化的&#xff1f;答案是三级缓存。 三级缓存 SpringIOC 通过三级缓存来解决循环依赖问题&#xff0c;三级缓存指的是三个Map&#xff1a; singletonObjects&#xff1a;一级缓存&#xf…

pythondocx模板_使用python-docx-template修改word文档

由于最近工作中需要自动修改word文档&#xff0c;并生成PDF文件&#xff0c;经过查阅资料后发现使用python-docx-template可以完成对word的修改工作&#xff0c;于是记录一下使用方法。文章内容大部分来自对以下博客的整理和学习https://blog.csdn.net/weixin_42670653/article…

面试必问的 CAS ,要多了解

转载自 面试必问的 CAS &#xff0c;要多了解前言 CAS&#xff08;Compare and Swap&#xff09;&#xff0c;即比较并替换&#xff0c;实现并发算法时常用到的一种技术&#xff0c;Doug lea大神在java同步器中大量使用了CAS技术&#xff0c;鬼斧神工的实现了多线程执行的安全性…

MySQL 对于千万级的大表要怎么优化?

很多人第一反应是各种切分&#xff1b; 我给的顺序是: 第一 优化你的sql和索引&#xff1b; 第二 加缓存&#xff0c;memcached,redis&#xff1b; 第三 以上都做了后&#xff0c;还是慢&#xff0c;就做主从复制或主主复制&#xff0c;读写分离&#xff0c;可以在应用层做&…

MySQL元数据库——information_schema

转自&#xff1a; https://www.cnblogs.com/postnull/p/6697077.html 平时使用MySQL客户端操作数据库的同学&#xff0c;只要稍微留神都会发现&#xff0c;除了我们建的库之外&#xff0c;还经常看到三个数据库的影子&#xff1a; 1. information_schema 2. performance_sche…

mysql 表字段信息从一张表迁移到另一张表_MySQL(数据库)笔记

###数据库之前通过流去操作文件保存数据库的弊端:1.执行效率低2.开发成本高3.一般只能保存小量数据4.只能保存文本数据####什么是DB- DataBase 数据库:代表文件集合####什么是DBMS- DataBaseManagementSystem 数据库管理系统(软件),用于管理保存数据的文件集合,用于和程序员进行…

GET与POST传递数据的最大长度能够达到多少

各种web开发语言中&#xff0c;各个页面之间基本都会进行数据的传递&#xff0c;web开发里面比较常用的数据传递方式有get post&#xff0c;一直以来我都只知道get传递的数据量要比post传递的数据量要少&#xff0c;所以传递大数据量还是要用post&#xff0c;但是 get post 这两…

maven命令实战

【1】 创建maven项目 1&#xff09;目录结构 mavenhello09|---src|---|---main|---|---|---java|---|---|---resources|---|---test|---|---|---java|---|---|---resources|---pom.xml 目录结构说明&#xff1a; main/java&#xff1a;主程序&#xff1b;main/resources&…

Mac 环境变量配置

环境变量配置 cd ~ (回到主目录home)如果你是第一次配置环境变量&#xff0c;可以使用“touch .bash_profile” 创建一个.bash_profile的隐藏配置文件vim .bash_profile&#xff0c;写入相应的环境变量&#xff0c;如下&#xff1a; # golang配置 export GOROOT/usr/local/Ce…