前言:各位老铁好,在前面博客中,笔者分享了有关二叉树的博客,在那篇博客中,笔者讲到了完全二叉树的存储结构中有两种存储方式,一种是顺序存储,一种是链式存储,链式存储笔者已经带各位老铁实现过了,今天笔者要分享的是,完全二叉树的顺序存储之堆。

1.堆的定义

那么什么是堆呢?我们肯定知道堆是一个完全二叉树,但是堆还需要满足一个条件,那就是每一个父节点的值必须大于等于其所有子节点的值(大根堆),又或者是每一个父节点的值小于等于其所有子节点的值(小根堆)
堆的图示
在这里插入图片描述
这里提一嘴,堆的实际的存储结构是数组来存储,我们一般把堆的逻辑结构看成是特殊的完全二叉树!!!

那么讲完了什么是堆,接下来就到堆实现的重头戏了。
再将堆结构的实现之前,我们先来铺垫两个概念
一个是向下调整算法,一个是向上调整算法

2.向下调整算法:向下调算法是有其中某一个非叶子节点不满足堆的性质,从而将它进行向下不断地调整到叶子节点,直到该节点满足堆地性质,向下调整算法有一个前提,该节点地左右子树必须满足堆的性质

可能有老铁看完笔者文字描述向下调整算法,对向下调整算法还不清楚,没关系,笔者接下来会举例加画图来给各位老铁讲解。
在这里插入图片描述
基于上面的内容,相信各位老铁一定懂得了向下调整算法了,那么接下来我们就需要来实现出向下调整算法,毕竟光说不练那是假把式嘛!(这里实现的是小根堆的向下调整算法)
我们知道堆的实际存储结构是数组,那么我们需要给堆传入一个数组,还需要有一个非叶子节点,因为我们是对非叶子节点进行调整嘛。(现在假设这个节点是根节点)
先定义一个结构体来表示堆(c语言实现,默认堆存储的整形数据)

#include <stdio.h>
typedef struct Heap
{int* _data;//存放数据int _size;//堆里面元素个数int _capacity;//整个空间大小
}Heap;

那么接下来就是实现向下调整算法了

typedef struct Heap
{int* _data;//存放数据int _size;//堆里面元素个数int _capacity;//整个空间大小
}Heap;//交换两个节点值
void Swap(int* h1, int* h2)
{int tmp=0;tmp = *h1;*h1 = *h2;*h2 = tmp;
}//n表示堆元素个数
void AdjustDowns(int* a, int n, int root)
{int parent = root;int child = parent * 2 + 1;while (child < n)//只要chlid节点还在堆范围内,就继续判断{//将parent节点和左右子节点进行比较if (child + 1 < n && a[child + 1] < a[child])//右子节点小于左子节点就++chlid{++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);//交换两个节点//更新chlid和parentparent = child;child = parent * 2 + 1;}//比父节点大不需要交换else{break;}}
}

3.向上调整算法:从某个节点开始,不断与父节点比较和交换,直到该节点满足堆的性质或者到达根节点为止,也需要满足除了这个节点的位置,前面的位置已经构成堆了。

笔者认为这个文字描述老铁们应该能看的懂了,所以笔者就不再画图了。
直接来实现向上调整算法了

//向上调整算法
void AdjustUp(int* a, int child)
{//先算出parent节点位置int parent = (child - 1) / 2;//减一是节点下标从0开始while (child > 0)//若该节点不是根节点就继续调整{if (a[child] < a[parent]){Swap(&a[child], &a[parent]);//更新child和parentchild = parent;parent = (child - 1) / 2;}else{break;}}
}

到这里我们就已经懂得了向上和向下调整算法了,下面我们就开始真正实现堆了。

4.初始化堆

void HeapInit(Heap* php)
{assert(php);//确保传入的对象不为空php->a = NULL;php->capacity = php->size = 0;
}

5.堆的插入

我们要明白堆的插入分为几种情况,是不是分为两种情况,一种是当存放数据的数组满了,是不是需要扩容才能插入,另一种是数组空间还足够,可以直接插入。

//堆的末尾插入数据
void HeapPush(Heap* php, int x)
{assert(php);//空间满了(按两倍来扩容)if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;int* tmp = (int*)realloc(php->a, sizeof(int) * newcapacity);if (tmp < 0){perror("realloc fail");//perror打印错误信息}php->a = tmp;php->capacity = newcapacity;}//插入数据php->a[php->size] = x;php->size++;//进行向上调整AdjustUp(php->a, php->size - 1);
}

6.取堆顶的数据

这个很简单,直接上代码了

int HeapTop(Heap* php)
{assert(php);assert(php->size);return php->a[0];
}

7.删除堆顶数据

如果我们直接删除堆顶的数据,是不是会破坏完全二叉树的结构,那么我们就换一种思路,通过将最后一个节点和第一个节点进行交换,然后在删除最后一个节点,最后再将第一个节点进行向下调整,这样就保持住了完全二叉树的结构。

void HeapPop(Heap* php)
{assert(php);assert(php->size > 0);//交换堆顶元素和最后一个元素Swap(&php->a[0], &php->a[php->size - 1]);//删除最后一个元素php->size--;//最后将第一个节点向下调整AdjustDown(php->a, php->size, 0);
}

8.判断堆是否为空

这个也很简单,直接上代码

bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}

9.释放堆

void HeapDestroy(Heap* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = php->size = 0;
}

10.有关于堆常考的一个面试题:Topk问题

Topk问题:假设现在有n个数,需要让你选出最小的前k个数,你该如何选?

法一:使用堆排序,先建立一个大堆(建堆时间复杂度是0(n)),再让堆顶元素和最后一个元素进行交换,我们知道堆顶元素是当前的最大值,那么每一次交换到当前的最后一个元素,就确保了当前堆顶元素放到该放的位置,进行n-1次进行交换,那么就整个有序了,那么总的时间复杂度是o(n*logn)

法二:我们可以先建立有k个元素的最大堆(堆顶是当前k个元素中的最大值),那么现在遍历剩下的n-k个元素,如果比堆顶元素小,那么就替换堆顶,然后再调整,直到替换出最小的前k个元素。

法三:假设现在n=10亿,我的内存肯定是存不下了,那么就不能直接使用堆排序了,那么这些值现在在文件中,那么我们该如何找出前k个最小元素

答:也是通过建立k个元素的大堆,再拿10亿-k的数据和堆顶数据比较,比堆顶小就替换堆顶并调整,最后一直比较完10亿-k个数据,就能找到最小的前k个元素了,那么我的内存仅仅需要维护o(k)

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

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

相关文章

通过针刺!鹏辉能源移动电源电池革新之作 Secu 系列:不燃电解液加持,充电宝安全新选择

9月11日&#xff0c;鹏辉能源对外发布新一代移动电源高安全电池Secu系列。该产品通过采用不燃的电解液破解移动电源产品安全难题&#xff0c;直击当下移动电源安全事故频发的行业痛点&#xff0c;为移动电源行业带来更安全、更可靠的半固态电池解决方案。数字化时代&#xff0c…

软件定义汽车(SDV)与区域电子电气架构(Zonal EEA)的技术革新

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

在 Docker Compose 中解决文件权限不足的问题

在使用 Docker 和 Docker Compose 构建应用时&#xff0c;由于容器中的文件权限不足而导致某些容器可能无法访问宿主机上的文件&#xff0c;或者容器内的文件系统无法正确读取或写入文件。问题描述在我的项目中&#xff0c;我使用 Docker Compose 来启动多个服务&#xff0c;并…

认知语义学对人工智能自然语言处理的深层语义分析:理论启示与实践路径

摘要随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;自然语言处理&#xff08;NLP&#xff09;已成为其核心驱动力之一。然而&#xff0c;尽管以大型语言模型&#xff08;LLMs&#xff09;为代表的现代NLP系统在处理语言任务上取得了前所未有的成功&#xf…

React19 中的交互操作

需要安装的库 antd-mobile、use-immer在App.jsx 中引入组件 Actionimport "./App.css" import Action from "./pages/action" function App() {return (<><Action></Action></>) }export default Appaction.jsx 组件import LearnI…

仓颉编程语言青少年基础教程:数组类型

仓颉编程语言青少年基础教程&#xff1a;数组类型 数组本质上是有序、同类型数据的集合容器&#xff0c;其核心作用是高效组织、访问和处理批量数据&#xff0c;同时结合语言特性&#xff0c;为开发者提供简洁、高性能的数据管理方式。例如&#xff1a; main() { let v1: …

C++微基础蓝桥杯之旅9.9-9.12

这里主要还是强制类型转换的使用//打印字符ASCII码值 //输入一个除空格以外的可见字符 //输出其ASCII值--十进制整数 #include <iostream> using namespace std;int main() {char ch;cin >> ch;//字符cout << (int)ch << endl; return 0; }//打印字符…

逻辑漏洞(上)- 突破功能限制漏洞、用户信息泄露(逻辑漏洞入门)

漏洞介绍&#xff1a; 在网络攻防实战中&#xff0c;常会遇到各种前端限制&#xff0c;绕过限制的方法大多是改包或者修改前端代码来实现的。 漏洞环境&#xff1a;docker docker-compose up -d 启动环境后&#xff1a;访问 http://127.0.0.1:8983/web/# 发现查询按钮是无法使用…

tsv文件简介

初步了解tsv文件在很多 OCR&#xff08;光学字符识别&#xff09;项目中&#xff0c;.tsv文件是标准的训练数据标注文件&#xff0c;主要用于存储 “图像路径 - 对应文本标签” 的映射关系&#xff0c;同时可能包含图像尺寸、文本长度等辅助信息&#xff0c;方便模型读取训练数…

apache poi 导出复杂的excel表格

如何导出复杂的excel 表格 如图表格&#xff0c;存在行和列的合并&#xff0c;边框&#xff0c;样式&#xff0c;颜色等。依赖<!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><dependency><groupId>org.apache.poi</groupId><arti…

下载 Eclipse Temurin 的 OpenJDK 提示 “无法访问此网站 github.com 的响应时间过长”

打开 Eclipse Temurin 的 OpenJDK 的官网下载地址&#xff1a; https://adoptium.net/zh-CN/temurin/releases 问 deepseek&#xff1a; 国内网络&#xff0c;打不开github.com网页&#xff0c;提示github.com 的响应时间过长。 国内无法访问 GitHub 或访问缓慢&#xff0c;通…

C/C++类型转换

C/C类型转换 1. C类型转换 C 语言中的类型转换主要分为两种&#xff1a;隐式类型转换 (Implicit Conversion) - 由编译器自动完成。显式类型转换 (Explicit Conversion) - 由程序员强制指定&#xff0c;也称为强制类型转换。1.2 隐式类型转换 编译器在编译时自动进行的转换&…

【Java】Windows切换Java8和Java11

现在有些项目要升级到Java17, 所以需要切换不同的java版本。 如何安装Java8 由于已经安装了jJava8, 之前的安装文章&#xff1a;【Java】jdk8安装——英文版 如何安装Java17 Java17下载地址 https://www.oracle.com/java/technologies/downloads/#java17-windows 下载到电…

SQLite 数据库核心知识与 C 语言编程

一、数据库基础概念1.1 数据库分类根据规模和应用场景&#xff0c;数据库可分为以下几类&#xff1a;大型数据库&#xff1a;Oracle&#xff08;适用于企业级高并发、大容量场景&#xff09;中型数据库&#xff1a;MySQL、MSSQL&#xff08;适用于中小型系统、Web 应用&#xf…

Netty 调优篇:实战配置、性能监控与常见坑

&#x1f680; Netty 调优篇&#xff1a;实战配置、性能监控与常见坑前面我们已经深入了 Netty 的 线程模型、Pipeline、EventLoop、内存池、零拷贝和背压机制。 但在实际工作中&#xff0c;很多人踩坑的地方不是“源码没看懂”&#xff0c;而是 调优没做好。 今天我们就从三个…

Linux Node.js 安装及环境配置详细教程

如果您喜欢此文章&#xff0c;请收藏、点赞、评论&#xff0c;谢谢&#xff0c;祝您快乐每一天。 一、Node.js是什么 Node.js是一个基于Chrome V8引擎的[JavaScript运行环境]。 Node.js使用了一个事件驱动、非阻塞式I/O 的模型。 Node.js是一个让JavaScript运行在服务端的开…

呼叫中心系统IVR流程设计的心理学

呼叫中心的 IVR&#xff08;交互式语音应答&#xff09;系统看似是 “机器与用户的对话”&#xff0c;实则暗藏对用户心理的精准把握。其设计需围绕降低焦虑、提升效率、强化信任三大核心目标&#xff0c;背后依托认知心理学、行为心理学、情感心理学等理论支撑。一、认知负荷理…

一些开源或免费的网络管理工具

整理开源及免费网络管理工具推荐,涵盖监控、配置、安全、流量分析等场景,适用于不同规模的网络环境: ​一、网络监控与性能分析​ 1. ​Zabbix​ ​特点​:企业级监控方案,支持SNMP、IPMI、JMX等多种协议,提供实时仪表盘、告警通知和自动化发现功能。 ​适用场景​:服…

谷粒商城项目-P16快速开发-人人开源搭建后台管理系统

1.对脚手架工程进行改造 此项目选用的脚手架工程是人人开源 地址&#xff1a;人人开源 选择的是下图标红的renren-fast作为后端&#xff0c;renren-fast-vue作为前端 克隆上述两个项目 2.后端改造 2.1将renrenfast项目的git文件夹删除后&#xff0c;拖进后端代码文件夹中 2…

V少JS基础班之第八弹:this

文章目录一、 前言二、本节涉及知识点三、重点内容1、从新的角度认识this2、this是函数的参数3、this的值4、函数的调用1- 裸函数调用2- 函数作为构造函数调用3- 函数作为对象的方法调用4- 函数显示调用5- 箭头函数一、 前言 第八弹内容是this。this相对来说难度不大&#xff…