二叉堆与优先队列

news2024/10/1 5:31:58

二叉堆与优先队列

1、什么是二叉堆

1.1、初识二叉堆

什么是二叉堆?

二叉堆本质上是一种完全二叉树,它分为两个类型。

  • 最大堆(也叫大顶堆):任意节点的值都大于或等于它的左右孩子节点的值,并且最大的值位于堆顶,即根节点处。

  • 最小堆(也叫小顶堆):任意节点的值都小于或等于它的左右孩子节点的值,并且最小的值位于堆顶,即根节点处。

image-20231127165040757

二叉堆的根节点叫做堆顶

最大堆和最小堆的特点决定了:最大堆的堆顶是整个堆中的最大元素;最小堆的堆顶是整个堆中的最小元素

1.2、二叉堆的自我调整

对于二叉堆,有如下的几种操作:

  1. 插入节点。
  2. 删除节点。
  3. 构建二叉堆。

这几种操作都是基于堆的自我调整,即把一个不符合堆性质的完全二叉树,调整成一个堆。下面以最小堆为例,来看一看二叉堆是如何进行自我调整的。

1.2.1、插入节点

当二叉堆插入新节点时,插入的位置是完全二叉树的最后一个位置。例如插入一个新节点0.

image-20231127170008913

这时新节点的父节点5比0大,不符合最小堆的性质,于是让新节点0上浮,和父节点交换位置。

image-20231127170117470

继续用节点0和它现在的父节点3比较,因为比3小,所以新节点0继续上浮。

image-20231127170215672

继续比较,最终新节点0上浮到了堆顶位置。

image-20231127170250838

总结一下堆插入元素的规则:将元素插入到保持其为完全二叉树的最后一个位置,然后顺着这条支路一直向上调整,每前进一层就要保证其子树都满足堆否则就去处理子树,直到完全满足要求。

1.2.2、删除节点

堆本身比较特殊,一般对堆中的数据进行操作都是针对堆顶的元素,即每次都是从堆中获得最大值或最小值,其他的不关心,所以删除节点的时候,也是删除堆顶。

例如删除最小堆的堆顶1

image-20231127170748599

这时,为了继续维持完全二叉树的结构,我们会把堆的最后一个节点10临时补到原本堆顶的位置。

image-20231127170838161

接下来,让暂处堆顶的节点和它的左右孩子进行比较,如果左右孩子节点中最小的一个比节点10小,那么让节点10下沉

image-20231127171009285

继续比较节点10与其孩子节点,继续下沉,直到满足最小堆要求

image-20231127171115073

这样一来,二叉堆重新得到了调整。

1.2.3、构建二叉堆

构建二叉堆,就是把一个无序的完全二叉树调整为二叉堆,本质就是让所有非叶子节点依次下沉

下面举一个无序完全二叉树构造成二叉堆的例子,如下图所示。

image-20231127171617135

首先,从最后一个非叶子节点开始,也就是从节点10开始。如果节点10大于它左、右孩子节点中最小的一个,则节点10“下沉”。

image-20231127171645772

接下来轮到节点3,如果节点3大于它左、右孩子节点中最小的一个,则节点3“下沉”。

image-20231127171708389

然后轮到节点1,如果节点1大于它左、右孩子节点中最小的一个,则节点1“下沉”。事实上节点1小于它的左、右孩子,所以不用改变。

接下来轮到节点7,如果节点7大于它左、右孩子节点中最小的一个,则节点7“下沉”。

image-20231127171745458

节点7继续比较,继续“下沉”。

image-20231127171804863

经过上述几轮比较和“下沉”操作,最终每一节点都小于它的左、右孩子节点,一个无序的完全二叉树就被构建成了一个最小堆。

1.3、二叉堆的代码实现

二叉堆虽然是一个完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。换句话说,二叉堆的所有节点都存储在数组中。

image-20231127172047412

我们可以通过数组的下标来定位父节点和左右孩子节点。

假设一个节点的下标为i:

  1. 当 i = 0 时,为根节点。
  2. 当 i >= 1 时,父节点为(i - 1) / 2。
  3. 左孩子节点为2 * i + 1,右孩子节点为2 * i + 2。

二叉堆具体的操作代码如下:

/**
 * “上浮”调整
 * @param array 待调整的堆
 */
public static void upAdjust(int[] array) {
	int childIndex = array.length-1;
	int parentIndex = (childIndex-1)/2;
	// temp 保存插入的叶子节点值,用于最后的赋值
	int temp = array[childIndex];
	while (childIndex > 0 && temp < array[parentIndex]){
		//无须真正交换,单向赋值即可
		array[childIndex] = array[parentIndex];
		childIndex = parentIndex;
		parentIndex = (parentIndex-1) / 2;
	}
	array[childIndex] = temp;
}

/**
 * “下沉”调整
 * @param array 待调整的堆
 * @param parentIndex 要“下沉”的父节点
 * @param length 堆的有效大小
 */
public static void downAdjust(int[] array, int parentIndex, int length) {
	// temp 保存父节点值,用于最后的赋值
	int temp = array[parentIndex];
	int childIndex = 2 * parentIndex + 1;
	while (childIndex < length) {
		// 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子
		if (childIndex + 1 < length && array[childIndex + 1] < array[childIndex]) {
			childIndex++;
		}
		// 如果父节点小于任何一个孩子的值,则直接跳出
		if (temp <= array[childIndex])
			break;
		//无须真正交换,单向赋值即可
		array[parentIndex] = array[childIndex];
		parentIndex = childIndex;
		childIndex = 2 * childIndex + 1;
	}
	array[parentIndex] = temp;
}

/**
 * 构建堆
 * @param array 待调整的堆
 */
public static void buildHeap(int[] array) {
	// 从最后一个非叶子节点开始,依次做“下沉”调整
	for (int i = (array.length-2)/2; i>=0; i--) {
		downAdjust(array, i, array.length);
	}
}

代码中有一个优化的点,就是在父节点和孩子节点做连续交换时,并不一定要真的交换,只需要先把交换一方的值存入temp变量,做单向覆盖,循环结束后,再把temp的值存入交换后的最终位置即可。

2、什么是优先队列

2.1、优先队列的特点

优先队列:说到底还是一种队列,他的工作就是poll()/peek()出队列中最大/最小的元素,所以叫带有优先级的队列。能够实现优先功能的策略不一定只有堆,例如二顶堆、平衡树、线段树、C++里会用二进制分组的vector来实现一个优先队列。

2.2、实现优先队列

当我们用二叉堆来实现优先队列时,优先队列的入队和出队操作就对应了二叉堆中的插入元素和删除元素,具体的代码如下:

private int[] array;
private int size;
public PriorityQueue(){
	//队列初始长度为32
	array = new int[32];
}

/**
 * 入队
 * @param key 入队元素
 */
public void enQueue(int key) {
	//队列长度超出范围,扩容
	if(size >= array.length){
		resize();
	}
	array[size++] = key;
	upAdjust();
}

/**
 * 出队
 */
public int deQueue() throws Exception {
	if (size <= 0) {
		throw new Exception("the queue is empty !");
	}
	//获取堆顶元素
	int head = array[0];
	//让最后一个元素移动到堆顶
	array[0] = array[--size];
	downAdjust();
	return head;
}

/**
 * “上浮”调整
 */
private void upAdjust() {
	int childIndex = size-1;
	int parentIndex = (childIndex-1)/2;
	// temp 保存插入的叶子节点值,用于最后的赋值
	int temp = array[childIndex];
	while (childIndex > 0 && temp > array[parentIndex]) {
		//无须真正交换,单向赋值即可
		array[childIndex] = array[parentIndex];
		childIndex = parentIndex;
		parentIndex = parentIndex / 2;
	}
 	array[childIndex] = temp;
}

/**
 * “下沉”调整
 */
private void downAdjust() {
	// temp 保存父节点的值,用于最后的赋值
	int parentIndex = 0;
	int temp = array[parentIndex];
	int childIndex = 1;
	while (childIndex < size) {
		// 如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
		if (childIndex + 1 < size && array[childIndex + 1] > array[childIndex]) {
			childIndex++;
		}
		// 如果父节点大于任何一个孩子的值,直接跳出
        if (temp >= array[childIndex])
			break;
		//无须真正交换,单向赋值即可
		array[parentIndex] = array[childIndex];
		parentIndex = childIndex;
		childIndex = 2 * childIndex + 1;
	}
	array[parentIndex] = temp;
}

/**
 * 队列扩容
 */
private void resize() {
	//队列容量翻倍
	int newSize = this.size * 2;
	this.array = Arrays.copyOf(this.array, newSize);
}

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

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

相关文章

SHAP(一):具有 Shapley 值的可解释 AI 简介

SHAP&#xff08;一&#xff09;&#xff1a;具有 Shapley 值的可解释 AI 简介 这是用 Shapley 值解释机器学习模型的介绍。 沙普利值是合作博弈论中广泛使用的方法&#xff0c;具有理想的特性。 本教程旨在帮助您深入了解如何计算和解释基于 Shapley 的机器学习模型解释。 我…

NX二次开发UF_CURVE_create_arc_point_tangent_radius 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_create_arc_point_tangent_radius Defined in: uf_curve.h int UF_CURVE_create_arc_point_tangent_radius(tag_t point, tag_t tangent_object, double radius, UF_CURVE_…

初识前后端数据交互(新手篇)

一个软件项目的开发必然是离不开前端和后端的协作&#xff0c;对于刚入行的新手前端或者新手后端来说&#xff0c;很有必要了解一下对方是在做什么&#xff0c;以及提供给自己什么样的帮助&#xff0c;为什么需要对方共同协作才能完成整个软件项目的开发呢&#xff1f;希望这篇…

Scrapy框架内置管道之图片视频和文件(一篇文章齐全)

1、Scrapy框架初识&#xff08;点击前往查阅&#xff09; 2、Scrapy框架持久化存储&#xff08;点击前往查阅&#xff09; 3、Scrapy框架内置管道 4、Scrapy框架中间件&#xff08;点击前往查阅&#xff09; Scrapy 是一个开源的、基于Python的爬虫框架&#xff0c;它提供了…

2015年五一杯数学建模B题空气污染问题研究解题全过程文档及程序

2015年五一杯数学建模 B题 空气污染问题研究 原题再现 近十年来&#xff0c;我国 GDP 持续快速增长&#xff0c;但经济增长模式相对传统落后&#xff0c;对生态平衡和自然环境造成一定的破坏&#xff0c;空气污染的弊病日益突出&#xff0c;特别是日益加重的雾霾天气已经干扰…

从0开始学习JavaScript--JavaScript对象继承深度解析

JavaScript中的对象继承是构建灵活、可维护代码的关键部分。本文将深入讨论JavaScript中不同的继承方式&#xff0c;包括原型链继承、构造函数继承、组合继承等&#xff0c;并通过丰富的示例代码展示它们的应用和差异。通过详细解释&#xff0c;大家可以更全面地了解如何在Java…

Shopee如何入驻?如何防封?

Shopee作为东南亚领航电商平台&#xff0c;面向东南亚蓝海市场&#xff0c;近年来随着东南亚市场蒸蒸日上&#xff0c;虾皮也吸引了大批量的跨境商家入驻。那么接下来就给想要入驻的虾皮小白一个详细的安全入驻教程。 一、商家如何入驻 虾皮与LAZADA最大的区别就是商家即卖家&…

RT-DETR改进 | 2023 | InnerEIoU、InnerSIoU、InnerWIoU、InnerDIoU等二十余种损失函数

论文地址&#xff1a;官方Inner-IoU论文地址点击即可跳转 官方代码地址&#xff1a;官方代码地址-官方只放出了两种结合方式CIoU、SIoU 本位改进地址&#xff1a; 文末提供完整代码块-包括InnerEIoU、InnerCIoU、InnerDIoU等七种结合方式和其AlphaIoU变种结合起来可以达到二十…

15、矩阵键盘密码锁

矩阵键盘密码锁 main.c #include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "MatrixKey.h"//初始化变量 unsigned char KeyNum; unsigned int Password,Count;void main() {//LCD屏幕初始化显示Password:LCD_Init();…

kafka的详细安装部署

简介&#xff1a; Kafka是一个分布式流处理平台&#xff0c;主要用于处理高吞吐量的实时数据流。Kafka最初由LinkedIn公司开发&#xff0c;现在由Apache Software Foundation维护和开发。 Kafka的核心是一个分布式发布-订阅消息系统&#xff0c;它可以处理大量的消息流&#…

matplotlib,DLL load failed: 找不到指定的模块

问题&#xff1a;import matplotlib mportError: DLL load failed: 找不到指定的模块 &#xff08;2023年11月28日&#xff09; 解决方法&#xff1a;具体是matplotlib版本不匹配&#xff0c;而且在线pip install numpy时因为在线下载numpy库中缺少DLL。 应该下载带有mkl的num…

利用ogr2ogr从PostGIS中导出/导入Tab/Dxf/Geojson等格式数据

ogr2ogr Demo Command 先查看下当前gdal支持的全部格式&#xff0c;部分gdal版本可能不支持PostGIS。 如出现PostgreSQL表名支持。 #全部支持的格式 ogrinfo --formats | sort #AVCBin -vector- (rov): Arc/Info Binary Coverage #AVCE00 -vector- (rov): Arc/Info E00 (ASC…

居家适老化设计第三十三条---卫生间之暖风

居家适老化是指为了满足老年人居住需求而进行的住房改造&#xff0c;以提供更加安全、舒适、便利的居住环境。在居家适老化中&#xff0c;暖风系统是一个重要的考虑因素。暖风系统可以提供温暖舒适的室内温度&#xff0c;对老年人来说尤为重要。老年人常常身体机能下降&#xf…

PHPExcel 导出Excel报错:PHPExcel_IOFactory::load()

背景 近期在做 excel文件数据导出时&#xff0c;遇到如下报错&#xff1a; iconv(): Detected an illegal character in input string场景&#xff1a;计划任务后台&#xff0c;分步导出 大数据 excel文件发现在加载文件时&#xff0c;会有报错 报错信息 如下&#xff1a; {&q…

Elasticsearch初识--CentOS7安装ES及Kibana

文章目录 一&#xff0e;前言二&#xff0e;介绍1.Elasticsearch2.Kibana 三&#xff0e;ES安装1.下载安装包2.解压、配置2.1 解压2.2 配置 3.启动3.1增加用户3.2启动 4.解决资源分配太少问题5.启动成功 四&#xff0e;Kibana安装1.下载安装包2.解压、配置2.1 解压2.2 配置2.2 …

使用char.js 柱形方式显示 一年12个月的最高气温与最低气温

<!DOCTYPE html> <html> <head><title>气温图表</title><script src"https://cdn.jsdelivr.net/npm/chart.js"></script><style>#myChart{width:800px;height: 400px;}</style> </head> <body>&l…

SQL注入-数据库基础/SQL语法

目录 一&#xff0c;数据库概述 1.1 数据库 1.2 了解 ACID 理论 1.3 识别数据库 二&#xff0c;SQL 语法基础 三&#xff0c;SQL语句实例 3.1 SQL基础语句 3.2 SQL高级语句 四&#xff0c;基于SQL注入理解语法/函数 4.1 语法 4.2 函数 五&#xff0c;目录数据库info…

百度人工智能培训第一天笔记

参加了百度人工智能初步培训&#xff0c;主要是了解一下现在人工智能的基本情况&#xff0c;以便后续看可以参与一些啥&#xff1f; 下面就有关培训做一些记录&#xff0c;以便后续可以继续学习。 一、理论基础部分 二、实际操作部分 主要学习的百度人工智能平台如下&#xf…

C++学习之路(八)C++ 用Qt5实现一个工具箱(增加一个粘贴板记录管理功能)- 示例代码拆分讲解

昨天&#xff0c;我们用 Qt5 实现了一个小工具箱的雏形《C 实现简单的Qt界面&#xff08;消息弹框、按钮点击事件监听&#xff09;》&#xff0c;但是没什么实用价值。为了增加点作用&#xff0c;我们今天就为这个小工具箱增加第一个小功能 「 粘贴板记录管理功能 」&#xff0…

如何正确选择爬虫采集接口和API?区别在哪里?

在信息时代&#xff0c;数据已经成为了一个国家、一个企业、一个个人最宝贵的资源。而爬虫采集接口则是获取这些数据的重要手段之一。本文将从以下八个方面进行详细讨论&#xff1a; 1.什么是爬虫采集接口&#xff1f; 2.爬虫采集接口的作用和意义是什么&#xff1f; 3.爬虫…