Java数据结构之堆

article/2025/11/7 15:40:48

堆的概念

  1. 堆逻辑上是一棵完全二叉树
  2. 堆物理上是保存在数组中
  3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
  4. 反之,则是小堆,或者小根堆,或者最小堆
  5. 堆的基本作用是快速找集合中的最值

二叉树的顺序存储

使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中。
一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费。
这种方式的用法就是堆的表示

顺序存储中的双亲与孩子的下标关系

已知双亲的下标:

  1. 左孩子下标 = 2*parent+1;
  2. 右孩子下标 = 2*parent+1;

已知孩子下标:

  1. 双亲下标 = (child - 1) / 2;

建堆与向上调整
在这里插入图片描述
在这里插入图片描述
将二叉树调整成大根堆

public class Heap {// 定义数组和数组的长度public int[] elem;public int usedSize;public Heap(){this.elem = new int[10];}/*** 将一棵树调整大堆的思路*  1.每棵子树都应该变成大堆*  2.最主要的逻辑:从最后一棵子树开始进行调整(从4下标开始)*  3.怎么调整*  4.定义子树父亲节点p 孩子节点c*  5.在父亲节点的左右两边找到左右孩子的最大值*  6.让左右孩子的c 最大值 跟父亲节点进行交换*  7.注意:让p走到c 让c继续走 发现c超出下标 那就证明这棵子树交换完了*  8.然后再调整上一个子树 【其实是换下标 换p换c 继续调整】 树的思想 下标的操作** */public void createHeap(int[] array){for (int i = 0; i < array.length; i++) {// 把数组的值拷贝给 elemthis.elem[i] = array[i];this.usedSize++;}// parent 就代表每棵子树的根节点// 如何拿到最后 最后一个节点的下标: array.length-1// 最后一个节点的父节点: (array.length-1-1)/2for (int parent = (array.length-1-1)/2; parent >= 0 ; parent--) {// 注意:第2个参数 每次调整的结束位置应该是:不超过数组的最大值,下标不取等于就行// this.usedSizeadjustDown(parent,this.usedSize);}}// root:每棵树的根值 len:数组长度private void adjustDown(int root, int len) {int parent = root;int child = 2*parent+1;// 在调整的过程中 child要小于len 防止数组越界// 只要能进循环 就代表至少有左孩子while (child < len){// 找到左右孩子的最大值// 1.前提是你得有右孩子// 先判断是否有有孩子,并且判断左右孩子哪个大,并保证child是两个孩子中值最大的那一个if(child+1 < len && this.elem[child] < this.elem[child+1]){// 保证child是两个孩子中值最大的那一个child++;}// 保证,child下标的数据 一定是左右孩子的最大值的下标if(this.elem[child] > this.elem[parent]){int tmp = this.elem[child];this.elem[child] = this.elem[parent];this.elem[parent] = tmp;// 先移动parentparent = child;// 再移动childchild = 2*parent+1;}else {// 当调整的过程中,孩子节点没有父亲节点的值大,那么此时说明这棵树已经是大根堆了break;}}}
}

时间复杂度
要计算最坏的时间复杂度,实际上就是一棵满二叉树,这样每棵树都会进行调整

  1. 每一层有多少个节点
  2. 时间复杂度与节点的高度h和每层节点的个数有关

T(n) = 2^0 * (h-1)+2^1 *(h-2)+…+2^(h-2) * 1

2*T(n) = 2^1 *(h-1)+ 2^2 * (h-2)+2^3 *(h-3)+…+2^(h-1) * 1

错位相减
T(n) = 2^h - 1 - h

假设共有n个节点 n = 2^h - 1
2^h = n+1 h = log2(n+1)

T(n) = n - log2(n+1)
当n慢慢变大的时候,整个表达式的结果就趋近于n

堆的应用-优先级队列

优先级队列数据结构:应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。

内部原理:优先级队列的实现方式有多种,但最常见的是使用堆来构建

入队列

  1. 首先按尾插方式放入数组
  2. 比较其和双亲的值的大小,如果双亲的值大,则满足堆的性质,插入结束
  3. 否则,交换其和双亲位置的值,重新进行2,3步骤
  4. 直接根节点
	// child是新插入数据的下标public void adjustUp(int child){int parent = (child-1)/2;while (child > 0){if(this.elem[child]>this.elem[parent]){int tmp = this.elem[parent];this.elem[parent] = this.elem[child];this.elem[child] = tmp;child = parent;parent = (child-1)/2;}else {break;}}}public void push(int val){if(ifFull()){this.elem = Arrays.copyOf(this.elem,2*this.elem.length);}this.elem[this.usedSize] = val;this.usedSize++;adjustUp(this.usedSize-1);}private boolean ifFull() {if(this.elem.length == this.usedSize){return true;}else {return false;}}

出队列(出堆顶 优先级最高的)

为了防止破坏堆的结构,删除时并不是将堆顶元素删除,而是用数组的最后一个元素替换堆顶元素,然后通过向下调整方式重新调整成堆。

	public void pop(){if(isEmpty()){return;}int tmp = this.elem[0];this.elem[0] = this.elem[this.usedSize-1];this.elem[this.usedSize-1] = tmp;this.usedSize--;adjustDown(0,this.usedSize);}public boolean isEmpty(){return this.usedSize==0;}

返回队首元素

	public int peek(){if(isEmpty()){// return -1;throw new RuntimeException();}return this.elem[0];}

堆的其他应用-TopK问题

问题:给100万个数据排序,取前10个最大的

  1. 基本思路:直接Array.sort();
  2. 建立一个堆,这个堆是大堆,取前10个。缺点是堆太大了,堆大小也是100万。
  3. 建立一个大小为K的小堆。将当前数组的前K个建立为小堆;遍历剩下的元素,依次和堆顶元素比较,如果当前i下标元素比堆顶元素大,就把i下标入队。

相反,如果找前K个最小的,建堆就是大堆。

从数组中找最小的K个数据

    public static void topK(int[] array,int k){PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2-o1;}});// 建立大堆for (int i = 0; i < array.length; i++) {if(maxHeap.size() < k){// 优先级队列会自动调整为大堆maxHeap.offer(array[i]);}else {int top = maxHeap.peek();if (top > array[i]){maxHeap.poll();maxHeap.offer(array[i]);}}}System.out.println(maxHeap);}

堆的其他应用-堆排序

从小到大排序 建立大堆

	public void heapSort(){int end = this.usedSize-1;while (end>0){int tmp = this.elem[0];this.elem[0] = this.elem[end];this.elem[end] =tmp;adjustDown(0,end);end--;}}

从大到小排序 建立小堆

LeetCode373. 查找和最小的 K 对数字

class Solution {public List<List<Integer>> kSmallestPairs(int[] nums1,int[] nums2,int k){PriorityQueue<List<Integer>> maxHeap = new PriorityQueue<>(k, new Comparator<List<Integer>>() {@Overridepublic int compare(List<Integer> o1, List<Integer> o2) {/*** o2 和 o1 存的是List的引用* */// o2 -o1 的变形return (o2.get(0)+o2.get(1)) - (o1.get(0)+o1.get(1));}});for (int i = 0; i < nums1.length; i++) {for (int j = 0; j < nums2.length; j++) {if(maxHeap.size()<k){List<Integer> pair = new ArrayList<>();pair.add(nums1[i]);pair.add(nums2[j]);maxHeap.offer(pair);}else {List<Integer> top = maxHeap.peek();// 如果当前元素 比 堆顶小 就弹出堆顶 然后将小的数据放入堆int topValue = top.get(0)+top.get(1);if(topValue > nums1[i]+nums2[j]){maxHeap.poll();List<Integer> pair = new ArrayList<>();pair.add(nums1[i]);pair.add(nums2[j]);maxHeap.offer(pair);}}}}List<List<Integer>> ret = new ArrayList<>();for (int i = 0; i < k && !maxHeap.isEmpty(); i++) {// 将堆弹出的元素放到ret中ret.add(maxHeap.poll());}return ret;}
}

http://chatgpt.dhexx.cn/article/2Iw9hWjm.shtml

相关文章

常用三极管引脚定义

给大家总结了常用封装的三极管的引脚定义 配套的视频讲解 常用三极管引脚定义

常用电子器件 ——三极管

说明&#xff1a;   本文章旨在总结备份、方便以后查询&#xff0c;由于是个人总结&#xff0c;如有不对&#xff0c;欢迎指正&#xff1b;另外&#xff0c;内容大部分来自网络、书籍、和各类手册&#xff0c;如若侵权请告知&#xff0c;马上删帖致歉。   QQ 群 号&#xf…

三极管开关特性

三极管在我们数字电路和模拟电路中都有大量的应用&#xff0c;在我们开发板上也用了多个三极管。在我们板子上的 LED 小灯部分&#xff0c;就有这个三极管的应用了&#xff0c;图 3-5 的 LED 电路中的 Q16就是一个 PNP 型的三极管。 图 3-5 LED 电路 三极管的初步认识 三极管…

常用三极管入门级电路设计(如何选型和搭电路?)

转自&#xff1a; 常用的三极管电路设计-电阻到底是怎么选的&#xff08;修正后&#xff09;-面包板社区 硬件基础知识---如何设计一个三极管放大电路_学无止境的专栏-CSDN博客_uce怎么算 今天的内容超级简单&#xff0c;主要给硬件新手写点东西&#xff0c;关于三极管实用方…

三极管相关知识点

1.两种类型的三极管图示如下&#xff0c;基极&#xff08;Base&#xff09;、集电极&#xff08;Collector&#xff09;和发射极&#xff08;Emitter&#xff09;&#xff0c;等效二极管电路如图所示 2.三极管的几种工作状态&#xff0c; 3.三极管做开关功能时&#xff0c;工…

三极管的一些基本知识

导通条件 NPN型三极管的导通条件是C点电位>B点电位>E点电位&#xff0c;三极管饱和导通的条件是Ub>Ue,Ub>Uc。 PNP型三极管的导通条件是E点电位>B点电位>C点电位&#xff0c;三极管饱和导通的条件是Ue>Ub,Uc>Ub。 NPN型三极管 1、定义&#xff1a; …

三极管的几点应用

关注星标公众号&#xff0c;不错过精彩内容 直接来源 | 巧学模电数电单片机 在复杂多变的模拟电路中&#xff0c;少不了学习三极管&#xff0c;过往的血泪史&#xff0c;不想再提&#xff0c;什么正偏反偏都给我统统滚蛋&#xff01;今天来点实际的。 三极管有三个工作状态&…

【三极管】

文章目录 一、图片&#xff08;NPN - PNP&#xff09;二、导通、截至、饱和条件&#xff08;一&#xff09;NPN&#xff08;二&#xff09;PNP 三、三极管的作用&#xff08;一&#xff09;开关作用 1、同或、异或、或非、与非—真值表 ① 同或&#xff1a;相同为1&#…

三极管常用封装的引脚排列

描述 三极管具有三个引脚&#xff0c;定义分别为基极b、集电极c、发射极e&#xff0c;在设计电路和设计封装时&#xff0c;这三个引脚的顺序必须和封装对应一致&#xff0c;否则电路无法正常工作&#xff0c;三极管常用的封装有&#xff1a;直插TO-92&#xff0c;贴片SOT-23、S…

电子元器件篇---三极管

目录 简介 三极管基本参数 2.1、封装形式 2.2、特征频率 2.3、工作电压/电流 2.4、电流放大倍数 2.5、集电极发射极反向击穿电压 2.6、最大允许耗散功率 三极管种类 三极管用途 4.1共射放大电路 4.2共集放大电路 4.3共基放大电路 4.4开关和反相电路 4.5稳压作用 简…

三极管的基础知识(上)

一、半导体三极管 1.作用 主要用于放大电路、开关电路。 2.三极管的结构与类型 &#xff08;1&#xff09;结构&#xff1a;三极管有两个PN结&#xff08;集电结、发射结&#xff09;、三个区&#xff08;集电区、基区、发射区&#xff09;、三个电极&#xff08;集电极、基…

三极管

三极管 一.三极管介绍参考资料 二. 三极管的作用1.开关来使用 2.二级驱动3.放大信号4.三极管还可以用来反向5.隔离参考来源 pnp型三极管电压总结 一.三极管介绍 三极管是最重要的电子元器件之一&#xff0c;成功制作世界上第一只半导体三极管的美国物理学家约翰巴丁(John Bard…

三极管与mos管通俗讲解

文章目录 一、三极管1.结构2.应用 二、mos管1.结构及原理2.mos管通断3.mos管最常用作用4.应用5.MOS管的栅极和源极之间的电阻 一、三极管 1.结构 电流控制型元器件&#xff0c;只要基极B有输入&#xff08;或输出&#xff09;电流就可以对这个晶体管进行控制了 三极管有三个…

三极管基础知识

二极管的导电特性 1. 正向偏置 在电子电路中&#xff0c;将二极管的正极接在高电位端&#xff0c;负极接在低电位端&#xff0c;二极管就会导通&#xff0c;这种连接方式&#xff0c;称为正向偏置。必须说明&#xff0c;当加在二极管两端的正向电压很小时&#xff0c;…

三极管基础分类, 参数选择及常见型号对比

三极管基础 具体的原理就不说了, N型和P型都是通过硅和锗参杂不同的元素带来的电子富余或缺少从而产生内在的电动势, 这里说的是如何帮助记忆. 半导体类型 P型半导体, p-type Semiconductor, p-type 这个名称代表的是 the positive charge of a hole, 知道这个就很好记忆了N…

常用三极管的区别 9012 9013 9014 9015 8550 8050

常用三极管的区别 9012 9013 9014 9015 8550 8050 转载自&#xff1a; http://hi.baidu.com/2000258051/blog/item/26f90b97f75ed66954fb961e.html 9013、9014均为NPN型三极管&#xff0c;最大耗散功率Pcm为0.625W&#xff1b;最大集电极电流Icm为0.5A&#xff1b;集电极-基极击…

常用三极管汇总

常用三极管汇总 一、插件三极管 型号基本参数类型封装应用说明实物图PcVCBOVCEOVEBOhFEIC2SC26550.9W50V50V5V--2ANPNTO-92L高速开关管&#xff0c;用于大电流PWM推挽驱动与2SA1020互补 2SC90130.625W40V20V5V84-2020.5ANPNTO-92与SS9012互补同KSP2222A2N55510.63W180V160V6V3…

React项目打包 优化详解

项目打包和优化-项目打包 目标&#xff1a;能够通过命令对项目进行打包 步骤&#xff1a; 在项目根目录打开终端&#xff0c;输入打包命令&#xff1a;npm run build 等待打包完成&#xff0c;打包后的内容被放在项目根下的 build 文件夹中 项目打包和优化-项目本地预览 …

React vscode 创建 react 项目流程【超详细】

文章目录 一、安装node二、配置淘宝镜像三、配置 vscode&#xff08;win10&#xff09;四、全局安装脚手架五、创建项目编写第一个 react 程序教程 一、安装node 请在官网下载安装&#xff1a;https://nodejs.org/zh-cn/vscode 中 点击 ( ctrl ) 调出终端输入指令node -v&…

react项目关闭eslint监测

前言 同事给我一个前端包&#xff0c;需要我在其他项目中复用该项目中的某些功能。拿到包之后就是npm install安装依赖。解决了node版本与依赖之间的冲突&#xff0c;到后面出现eslint监测运行不了项目&#xff0c;因此写下文章来记录。 解决方案&#xff1a; 在网上也同样找了…