数据与结构--堆

堆的概念

:如果有一个关键码的集合K={k0,k1,k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足ki<=k2i+1且ki<=k2i+2(或满足ki>=k2i+1且ki>=k2i+2),其中i=0,1,2,…,则称该集合为堆。

  1. 堆序性质:在堆中,父节点的值要么大于等于(最大堆)或小于等于(最小堆)其子节点的值。这个性质是堆的核心特征。

  2. 完全二叉树结构:堆通常是一棵完全二叉树,即除了最底层外,其他层的节点都是满的,而且最底层的节点都集中在最左边。这意味着在堆中插入和删除节点时,树的形状会发生变化,但始终保持完全二叉树的性质。

  3. 最大堆和最小堆:堆分为最大堆和最小堆。

    • 最大堆:每个父节点的值都大于等于其子节点的值。根节点是堆中的最大值。
    • 最小堆:每个父节点的值都小于等于其子节点的值。根节点是堆中的最小值。

堆的结构 

大根堆示例

小根堆示例

堆的向下调整算法

现在我们给出一个数组,逻辑上看作一棵完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。

 但是,使用向下调整算法需要满足一个前提
 若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
 若想将其调整为大堆,那么根结点的左右子树必须都为大堆。

向下调整算法的基本思想(以建小堆为例):
 1.从根结点处开始,选出左右孩子中值较小的孩子。
 2.让小的孩子与其父亲进行比较。
 若小的孩子比父亲还小,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。
 若小的孩子比父亲大,则不需处理了,调整完成,整个树已经是小堆了。

代码如下:

//交换函数
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}//堆的向下调整(小堆)
void AdjustDown(int* a, int n, int parent)
{//child记录左右孩子中值较小的孩子的下标int child = 2 * parent + 1;//先默认其左孩子的值较小while (child < n){if (child + 1 < n&&a[child + 1] < a[child])//右孩子存在并且右孩子比左孩子还小{child++;//较小的孩子改为右孩子}if (a[child] < a[parent])//左右孩子中较小孩子的值比父结点还小{//将父结点与较小的子结点交换Swap(&a[child], &a[parent]);//继续向下进行调整parent = child;child = 2 * parent + 1;}else//已成堆{break;}}
}

那么建堆的时间复杂度又是多少呢?
 当结点数无穷大时,完全二叉树与其层数相同的满二叉树相比较来说,它们相差的结点数可以忽略不计,所以计算时间复杂度的时候我们可以将完全二叉树看作与其层数相同的满二叉树来进行计算。

堆的向上调整算法

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。 

代码实现

//交换函数
void Swap(HPDataType* x, HPDataType* y)
{HPDataType tmp = *x;*x = *y;*y = tmp;
}//堆的向上调整(小堆)
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0)//调整到根结点的位置截止{if (a[child] < a[parent])//孩子结点的值小于父结点的值{//将父结点与孩子结点交换Swap(&a[child], &a[parent]);//继续向上进行调整child = parent;parent = (child - 1) / 2;}else//已成堆{break;}}
}

 堆的实现

初始化堆

// 初始化堆
HeapNode* initializeHeap() {return nullptr; // 返回空指针表示空堆
}

判断堆是否为空

// 判断堆是否为空
bool isEmpty(HeapNode* heap) {return heap == nullptr;
}

这个函数简单地检查堆是否为空,如果堆是空的则返回true,否则返回false。

插入元素

// 向堆中插入元素
HeapNode* insertElement(HeapNode* heap, int val) {HeapNode* newNode = new HeapNode(val); // 创建新节点if (isEmpty(heap)) {return newNode; // 如果堆为空,新节点即为根节点} else {// 找到最后一个节点HeapNode* temp = heap;while (temp->left != nullptr && temp->right != nullptr) {temp = temp->left; // 堆是一个完全二叉树,所以优先插入左子节点}// 插入新节点作为最后一个节点的左子节点if (temp->left == nullptr) {temp->left = newNode;} else {temp->right = newNode;}return heap;}
}

这个函数首先创建一个新节点,然后判断堆是否为空。如果堆为空,新节点即为根节点。如果堆不为空,函数会找到最后一个节点,然后将新节点插入为其左子节点(优先插入左子节点)或右子节点。

删除元素

// 从堆中删除元素
HeapNode* deleteElement(HeapNode* heap, int val) {if (heap == nullptr) {std::cout << "Heap is empty." << std::endl;return heap; // 如果堆为空,直接返回}// 先找到要删除的节点及其父节点HeapNode* parent = nullptr;HeapNode* nodeToDelete = heap;while (nodeToDelete != nullptr && nodeToDelete->value != val) {parent = nodeToDelete;if (val < nodeToDelete->value) {nodeToDelete = nodeToDelete->left;} else {nodeToDelete = nodeToDelete->right;}}// 如果未找到要删除的节点if (nodeToDelete == nullptr) {std::cout << "Element not found in heap." << std::endl;return heap;}// 如果要删除的节点有两个子节点if (nodeToDelete->left != nullptr && nodeToDelete->right != nullptr) {// 找到要删除节点的右子树中最小的节点HeapNode* minRight = nodeToDelete->right;while (minRight->left != nullptr) {minRight = minRight->left;}// 用最小右节点的值替换要删除的节点的值nodeToDelete->value = minRight->value;// 删除最小右节点heap = deleteElement(heap, minRight->value);return heap;}// 如果要删除的节点是叶子节点或只有一个子节点if (nodeToDelete->left == nullptr) {if (parent != nullptr) {if (parent->left == nodeToDelete) {parent->left = nodeToDelete->right;} else {parent->right = nodeToDelete->right;}} else {heap = nodeToDelete->right;}delete nodeToDelete;return heap;}if (nodeToDelete->right == nullptr) {if (parent != nullptr) {if (parent->left == nodeToDelete) {parent->left = nodeToDelete->left;} else {parent->right = nodeToDelete->left;}} else {heap = nodeToDelete->left;}delete nodeToDelete;return heap;}return heap;
}

删除元素代码解释

这段代码实现了从堆中删除元素的功能。让我来解释一下:

  1. 首先,我们检查堆是否为空,如果为空则输出错误信息并直接返回。

  2. 接着,我们使用循环来找到要删除的节点以及其父节点。循环条件是当前节点不为空且当前节点的值不等于待删除的值,根据待删除的值和当前节点值的比较结果来决定往左子树还是右子树走。

  3. 如果我们找到了要删除的节点:

    • 如果要删除的节点有两个子节点,则我们需要找到其右子树中的最小节点(即右子树中的最左下角的节点),将其值替换到待删除的节点中,然后递归地删除最小节点。

    • 如果要删除的节点是叶子节点或只有一个子节点,则我们将其子节点链接到其父节点上,并删除待删除的节点。

  4. 最后,我们返回调整后的堆。

打印元素

// 打印堆中的元素
void printHeap(HeapNode* heap) {if (isEmpty(heap)) {std::cout << "Heap is empty." << std::endl;return;}// 使用中序遍历打印堆中的所有元素printHeap(heap->left);std::cout << heap->value << " ";printHeap(heap->right);
}

销毁堆

// 销毁堆
void destroyHeap(HeapNode* heap) {if (heap != nullptr) {destroyHeap(heap->left); // 递归销毁左子树destroyHeap(heap->right); // 递归销毁右子树delete heap; // 释放当前节点内存}
}

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

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

相关文章

PostgreSQL自带的命令行工具22- pg_controldata

PostgreSQL自带的命令行工具22- pg_controldata 基础信息 OS版本&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本&#xff1a;16.2 pg软件目录&#xff1a;/home/pg16/soft pg数据目录&#xff1a;/home/pg16/data 端口&#xff1a;5777pg_controlda…

深度缓冲技术在AI去衣中的神奇作用

引言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;其在图形处理和视觉领域的应用日益增多。AI去衣技术便是其中一个颇具争议但又技术上引人入胜的话题。今天&#xff0c;我们将深入探讨一项关键技术——深度缓冲&#xff08;Depth Buffering&#xff09;&#xff0c;它…

MPLS技术基础

一、MPLS的基本概述 1、MPLS【Multi-Protocol Label Switch|多协议标签交换】 2、MPLS最早由Cisco System公司开发&#xff0c;最早早该技术称之为Tagging Switch&#xff0c;后被IETF&#xff08;internet 工程任务小组&#xff09;改名为MPLS&#xff0c;进而向业界公开推广…

SpringAI 技术解析

1. 发展历史 SpringAI 的发展历史可以追溯到对 Spring 框架的扩展和改进&#xff0c;以支持人工智能相关的功能。随着人工智能技术的快速发展&#xff0c;SpringAI 逐渐成为 Spring 生态系统中的一个重要组成部分&#xff0c;为开发者提供了便捷、灵活的解决方案。 项目的灵感来…

如何用opencv去掉单元格的边框线,以提高Tesseract识别率?

在OpenCV中处理从表格切割下来的图片&#xff0c;并去掉单元格的边框线&#xff0c;以提升Tesseract的识别准确率&#xff0c;确实是一个具有挑战性的任务。在这种情况下&#xff0c;我们需要采取一种策略来预处理图像&#xff0c;使得数字与背景之间的对比度增强&#xff0c;同…

数据缓存,可以尝试RocksDB了

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen shigen在最近的学习中&#xff0c;接触到了一款新的缓存数据库RocksDB&#xff…

NodeJS编写后端接口

技术栈 1.express&#xff1a;Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建 各种 Web 应用&#xff0c;和丰富的 HTTP 工具&#xff0c;使用 Express 可以快速地搭建一个完整功能的网站。 2.mysql&#xff1a;用于操作MySQL数据库 3.bod…

Spring:深入理解 Spring 事务原理

Spring 事务 Spring 框架支持两种主要的事务管理方式&#xff1a;编程式事务&#xff08;Programmatic Transaction Management&#xff09;和声明式事务&#xff08;Declarative Transaction Management&#xff09;。 1.编程式事务   编程式事务管理指的是在代码中显式地控…

使用 LangGraph 实现邮件智能处理系统来处理客户咨询【附代码】

简介 在这篇博文中&#xff0c;我们将探讨如何使用 LangGraph 结合 RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术来构建一个响应客户邮件的电子邮件智能系统。LangGraph 是一个强大的框架&#xff0c;专为构建和部署基于图的语言应用而设计。它支持复杂的查…

【Linux】centos7安装软件(rpm、yum、编译安装),补充:查找命令的相关文件路径,yum安装mysql

【Linux】技术上&#xff0c;Linux是内核。而术语上&#xff0c;我们通常说的Linux是完整的操作系统&#xff0c;其实称为"Linux发行版"&#xff0c;是将Linux内核和应用系统打包&#xff0c;由不同的发行家族发行了不同版本。Linux发行版众多&#xff0c;主要有RedH…

职校智慧校园现状及问题分析

各大中职院校及高职院校是校园信息化的先行者和开拓者&#xff0c;很早就开始注重信息化基础设施建设和信息化人文素养的提升。在过去几年里&#xff0c;随着国家大力发展与扶植职校教育&#xff0c;学校投入相当的经费进行了校园信息通信网络、计算机等基础硬件设备建设&#…

Python3 笔记:help()查看函数的用法

一些不常用的函数或是模块的用法记不清了怎么办&#xff1f; Python的内置函数help()可以查看函数或模块用途的详细说明。 操作方法很简单&#xff0c;直接在help()括号内填写参数&#xff0c;然后运行就可以看到结果了。 举例&#xff1a; help(input) # 查询input()函数的…

RabbitMQ 面试题(一)

1. 简述为什么要使用 RabbitMQ ? 使用 RabbitMQ 的主要原因包括以下几点&#xff1a; 解耦&#xff1a;在复杂的系统中&#xff0c;不同的服务或组件之间往往需要通信和协作。RabbitMQ 作为消息队列&#xff0c;允许这些组件或服务通过发送和接收消息来交互&#xff0c;而无…

CLion 写 Rust 报Project directory `/Users/.../rsheets` does not exist.

每次打开CLion都会看到像下面这样的报错&#xff0c;Project directory /Users/.../rsheets does not exist.&#xff0c;虽然不会影响你写代码&#xff0c;但每次看到还是不舒服&#xff0c;所以研究一下怎么解决。 原因是这样的&#xff0c;每当我们创建一个 Cargo 项目&…

PDF Squeezer for Mac,让PDF压缩更高效

还在为PDF文件过大而烦恼吗&#xff1f;试试PDF Squeezer for Mac吧&#xff01;它拥有强大的压缩功能&#xff0c;可以快速将PDF文件压缩至更小的体积&#xff0c;让你的文件传输更快捷。同时&#xff0c;它还支持多种压缩方式&#xff0c;满足你的不同需求。赶快下载体验吧&a…

革新机器人任务规划:TREE-PLANNER引领高效、准确的机器人动作生成新趋势

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; 引言 任务规划在机器人技术中扮演着至关重要的角色。它涉及到为机器人设计一系列中级动作&#xff08;技能&#xff09;&#xff0c;使其能够完成复杂的高级任…

C语言基础-枚举类型

C语言中的枚举类型&#xff08;enum&#xff09;是一种用户定义的数据类型&#xff0c;它允许为整数常量指定一个名称&#xff0c;使得代码更易于阅读和维护。枚举类型本质上是一种特殊的整数类型&#xff0c;但每个枚举值都有一个名字&#xff0c;而不是直接使用整数。 枚举类…

掌握 Linux Crontab:完整指南与实用案例

一.简介 1. 什么是 Crontab&#xff1f; Crontab 是一个在 Unix 和类 Unix 操作系统上用来定期执行任务的工具。它是从 “cron table” 衍生而来的&#xff0c;“cron” 是一个 Unix 系统自带的后台守护进程&#xff0c;用于定期执行预定的任务或命令&#xff0c;比如备份文件…

电视剧电影原声背景音乐,经典影视配乐片段音效合集

一、素材描述 本套影视配乐素材&#xff0c;大小1.89G&#xff0c;27个压缩文件。 二、素材目录 宰相刘罗锅配乐片段.rar 影视配乐65首.rar 太极张三丰原声.rar 东邪西毒原声配乐15首.rar 东方不败之风云再起配乐24首.rar 东方不败原声配乐16首.rar 电影大话西游原声配…

python绘制圣诞树 如何用python绘制一个圣诞树

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.使用 四.总结 一.前言 圣诞树,是指用灯烛和装饰品把枞树或洋松装点起来的常青树。作为是圣诞节重要的组成元素之一,近代圣诞树起源于德国,后来逐步在世界范围内流行起来