【数据结构】堆:建堆/向下调整/上向调整/堆排序/TOK问题

文章目录

  • 前言
  • 堆的定义
    • 1.大小堆
    • 2.完全二叉树
  • 堆的实现
    • 堆的数据结构
    • 初始化
    • 销毁
    • 取堆顶元素
    • 判断堆是否为空
    • 父结点和子结点下标关系(重要)
  • 向下调整法-O(n)
    • 小堆版
    • 大堆版
  • 向上调整法-nlog(n)
  • 堆的插入和删除
    • 插入(调用向上调整)
    • 删除(调用向下调整)
  • 构建最大堆
    • 向上调整-直接插入法
    • 向下调整法建堆(从最后一个非叶子节点开始)
  • 构建最小堆
  • 性能分析
    • 时间复杂度
    • 关键字对比
    • 总结一下
  • 堆排序
  • TOK问题


前言

🐱个人主页: 代码探秘者
🐭C语言专栏:C语言
🐰C++专栏: C++ / STL使用以及模拟实现/C++11新特性
🐹数据结构专栏: 数据结构 / 十大排序算法
🐶 Linux专栏: Linux系统编程 / Linux网络编程(准备更新)
🌈喜欢的诗句:天行健,君子以自强不息.
pic_8da49713.png


pic_bf427633.png

堆的定义

1.大小堆

堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树

小堆:将根结点最小的堆叫做小堆,也叫最小堆或小根堆。
大堆:将根结点最大的堆叫做大堆,也叫最大堆或大根堆。
在这里插入图片描述
在这里插入图片描述

2.完全二叉树

在这里插入图片描述
1、完全二叉树:若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树。

2、满二叉树满二叉树是一种特殊的的完全二叉树,所有层的结点都是最大值。

堆的实现

堆的数据结构

pic_92823800.png

typedef int DataType;
typedef struct Heap
{DataType* a;int size;		//当前堆大小int capacity;	//容量
}HP;

初始化

void HPInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}

销毁

//销毁
void HPDestory(HP* hp)
{if (hp->a){free(hp->a);hp->size = hp->capacity = 0;}
}

取堆顶元素

//返回堆顶元素
DataType HPTop(HP* hp)
{assert(hp);assert(!HPEmpty(hp));	//堆不为空return hp->a[0];
}

判断堆是否为空

//判断是否为空
bool HPEmpty(HP* hp)
{assert(hp);return hp->size == 0;
}

父结点和子结点下标关系(重要)

在这里插入图片描述
注意:在二叉树中,我们本文默认根结点下标为0,若当前节点的下标为 i

  • 父节点的下标为 i/2
  • 左子节点的下标为 i*2
  • 右子节点的下标为i*2+1

当然也有人选择根结点下标为1

向下调整法-O(n)

  • 向下调整算法有一个前提:左右子树必须是一个堆,才能调整

小堆版

我们通过从根结点开始的向下调整算法可以把它调整成一个小堆

  • 向下调整算法有一个前提:左右子树必须是一个小堆,才能调整
  • 选出最小的左右孩子
  • 最小孩子比父亲小,就交换上来
  • 更新parent和minchild,一直循环到最后
int array[] = {27,15,19,18,28,34,65,49,25,37};

在这里插入图片描述

//向下调整:从哪里开始
//删除调用
void AdjustDown(DataType* a, int n, int parent)
{//先找到最小的孩子int minchild = 2 * parent + 1;	//假设左孩子最小while (minchild < n){//右孩子存在,而且小于左孩子if (minchild + 1 < n && a[minchild + 1] < a[minchild]){minchild++;		//更新最小为右孩子}//开始向下调整//小堆为例)if (a[minchild] < a[parent]){Swap(&a[minchild], &a[parent]);	//交换//更新parent和minchildparent = minchild;minchild = 2 * parent + 1;}else{break;}}
}

大堆版

就是变化一点而已
这里向下调整算法有一个前提:左右子树必须是一个大堆,才能调整

  • 选出最大的孩子
  • 最大孩子比父亲大,就交换上来
  • 更新parent和minchild,一直循环到最后
//向下调整:							从哪里开始
//删除调用
void AdjustDown(DataType* a, int n, int parent)
{//先找到最大的孩子int maxchild = 2 * parent + 1;	//假设左孩子最小while (maxchild < n){//右孩子存在,而且大于左孩子if (maxchild + 1 < n && a[maxchild+ 1] > a[maxchild]){maxchild++;		//更新最大为右孩子}//开始向下调整//大堆为例if (a[maxchild] > a[parent]){Swap(&a[maxchild], &a[parent]);	//交换//更新parent和minchildparent = maxchild;maxchild = 2 * parent + 1;}else{break;}}
}

向上调整法-nlog(n)

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。
pic_5569e33f.png
向上调整算法的基本思想(以建小堆为例):
 1.将目标结点与其父结点比较。
 2.若目标结点的值比其父结点的值小,则交换目标结点与其父结点的位置,并将原目标结点的父结点当作新的目标结点继续进行向上调整。若目标结点的值比其父结点的值大,则停止向上调整,此时该树已经是小堆了。

堆的插入和删除

在这里插入图片描述

插入(调用向上调整)

  • 当然要考虑扩容的问题

  • 每次插入都是将先将新数据放在数组最后

  • 如果堆的性质破坏,就从插入新节点进行向上调整

在这里插入图片描述

//入堆(保持插入后还是堆)
void HPPush(HP* hp, DataType a)
{assert(hp);//容量不足:扩容if (hp->capacity == hp->size){size_t newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;DataType* tmp = (DataType*)realloc(hp->a,sizeof(DataType) * newcapacity);if (tmp == NULL){perror("realloc fail");return;}hp->a = tmp;		//赋值弄回来hp->capacity = newcapacity;//新空间}hp->a[hp->size] = a;hp->size++;//从刚刚插入的结点位置,进行向上调整AdjustUp(hp->a, hp->size - 1);	//减-1,因为刚刚+1
}

删除(调用向下调整)

删除堆是删除堆顶的数据

  • 堆顶的数据根最后一个数据一换
  • 然后删除数组最后一个数据
  • 再从根结点进行向下调整算法
    在这里插入图片描述
//出堆
void HPPop(HP* hp)
{assert(hp);assert(!HPEmpty(hp));	//堆不为空//交换:堆顶元素移动最后面Swap(&hp->a[0],&hp->a[hp->size-1]);hp->size--;		//把最后一个元素删掉,元素个数减1//然后从根节点进行向下调整AdjustDown(hp->a,hp->size,0);}

构建最大堆

怎么建堆?

常用的方法:直接插入法向下调整(堆化)

向上调整-直接插入法

直接插入法:这种方法通过逐个插入元素,每次插入后进行向上或向下调整,直到整个数组满足堆的性质。虽然时间复杂度为O(nlogn),但它简单直观,适用于元素数量不多的情况

//建堆
void testHeap1()
{int a[] = { 50,100,70,65,60,32 };HP hp;int n = sizeof(a) / sizeof(a[0]);//插入法建堆-向上调建堆N*logNfor (int i = 1; i < n; i++){HPPush(&hp,a[i]);		//调用插入}
}
int main()
{testHeap1();return 0;
}

向下调整法建堆(从最后一个非叶子节点开始)

  • 自下而上,向下调整法建堆:从最后一个非叶子节点开始,即从数组的中间开始直到根节点

向下调整(堆化):这是构建最大堆和最小堆最常用的方法。它从最后一个非叶子节点开始,逐个进行向下调整,直到根节点。这种方法的时间复杂度为O(n),非常高效。

原始数据为a[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7},采用顺序存储方式,对应的完全二叉树如下图所示:
pic_7c2a1969.png

基本思想:
首先将每个叶子节点视为一个堆,再将每个叶子节点与其父节点一起构造成一个包含更多节点的对。

  • 最后一个非叶子结点下标:(n - 1 - 1) / 2(从0开始编号)

  • (a)在构造堆的时候,首先需要找到最后一个节点的父节点(就是最后一个非叶子结点),最后一个节点为7,其父节点为16从16这个节点开始构造最大堆

  • (b) 然后就继续16的上一个节点14,把这颗子树调整为大堆

  • (c)(d) (e): 一直调整到根节点

pic_4b0f0dfe.png

代码实现如下:

//建堆
void testHeap1()
{int a[] = { 50,100,70,65,60,32 };int n = sizeof(a) / sizeof(a[0]);// 向下调整建堆 O(N)for (int i = (n-1-1)/2; i >= 0; i--){AdjustDown(a, n, i);}
}
int main()
{testHeap1();return 0;
}

构建最小堆

只需要简单调用上面代码,整体操作和最大堆类似,这里不做赘述

性能分析

时间复杂度

向上调整法(Insertion)
向上调整法是在堆中插入一个新元素时使用的。新元素被插入到堆的末尾,然后如果它比父节点大(在最大堆中)或小(在最小堆中),则需要向上移动,直到它不再违反堆的性质或者到达根节点。

时间复杂度:O(log n)

这是因为在最坏的情况下,新元素可能需要一直移动到根节点,而一个完全二叉树的高度是log n级别的。

如果我们使用向上调整法来逐个插入元素以构建堆,那么我们需要对数组中的每个元素执行一次插入操作,总共需要执行 n 次插入操作,因此总的时间复杂度将是 O(n log n)

==向下调整法(Heapify)==的时间复杂度

向下调整法是在构建堆或者删除堆顶元素后,用于维护堆的性质。从根节点开始,如果根节点违反了堆的性质,就将它和子节点中最大的(在最大堆中)或最小的(在最小堆中)交换,然后对交换后的子树继续进行向下调整。

时间复杂度:O(log n)

这是因为在最坏的情况下,需要调整的节点可能一直到达叶子节点,而树的高度是log n级别的。

建堆的时间复杂度

建堆是指将一个无序的数组转换为一个堆结构。通常使用向下调整法(Heapify)来完成这个过程。

时间复杂度:O(n)

这个时间复杂度是基于这样一个事实:在构建堆的过程中,最后一个非叶子节点到根节点的向下调整操作的总时间复杂度是O(log n),而这样的节点有n/2个(对于n个节点的数组)。因此,总的时间复杂度是O(n/2 * log n),简化后为O(n)。

关键字对比

在这里插入图片描述

总结一下

堆的向下调整算法的时间复杂度O(log n)

建堆的时间复杂度: O(n log n)

堆的向上调整算法的时间复杂度O(log n)

建堆的时间复杂度: O(n)

堆排序

详细文章点这里

TOK问题

详细文章点这里

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

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

相关文章

java学习1

一、运算符 1.算术运算符 在代码中&#xff0c;如果有小数参与计算&#xff0c;结果有可能不精确 1-1.隐式转换和强制转换 数字进行运算时&#xff0c;数据类型不一样不能运算&#xff0c;需要转成一样的&#xff0c;才能运算 &#xff08;1&#xff09;隐式转换&#xff1a…

20.体育馆使用预约系统(基于springboot和vue的Java项目)

目录 1.系统的受众说明 2.开发环境与技术 2.1 Java语言 2.2 MYSQL数据库 2.3 IDEA开发工具 2.4 Spring Boot框架 3.需求分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.2 系统流程分析 3.3 系统性能需求 3.4 系统功能需求 4.系…

Halcon3D image_points_to_world_plane详解

分三个部分来聊聊这个算子 一,算子的参数介绍 二,算法的计算过程 三,举例实现 第一部分,算子的介绍 image_points_to_world_plane( : : CameraParam, WorldPose, Rows, Cols, Scale : X, Y) 参数介绍: CameraParam,:相机内参 WorldPose 世界坐标系,也叫物体坐标系(成…

【启程Golang之旅】并发编程构建简易聊天系统

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

无人机场景 - 目标检测数据集 - 夜间车辆检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;无人机场景夜间车辆检测数据集&#xff0c;真实场景高质量图片数据&#xff0c;涉及场景丰富&#xff0c;比如夜间无人机场景城市道路行驶车辆图片、夜间无人机场景城市道边停车车辆图片、夜间无人机场景停车场车辆图片、夜间无人机场景小区车辆图片、夜…

HTML学习笔记十

系列笔记目录 第一章 HTML的概述 第二章 URL简介 第三章 网页元素的属性 第四章 html字符编码 第五章 网页的语义结构 第六章 文本标签 第七章 列表标签 第八章 图像标签 第九章 链接标签 第十章 多媒体标签 多媒体标签 系列笔记目录前言一、简介二、常用标签2.1<video>2…

Thumb 汇编指令集,Thumb 指令编码方式,编译 Thumb 汇编代码

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ Thumb指令集 ARM 指令集&#xff1a;最早在 1985 年随第一代 ARM 处理器问世。ARM 指令集一开始是 32 位固定长度的指令&#xff0c;用于各种计算任务。 Thu…

【Clikhouse 探秘】ClickHouse 物化视图:加速大数据分析的新利器

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

【HarmonyOS NEXT】在 HarmonyOS NEXT 中实现优雅的加载动画

【HarmonyOS NEXT】在 HarmonyOS NEXT 中实现优雅的加载动画 在移动应用开发中&#xff0c;加载动画是提升用户体验的重要工具。在应用程序处理数据或加载页面时&#xff0c;为用户提供视觉反馈尤为关键。在这篇博客中&#xff0c;我们将探讨如何在 HarmonyOS NEXT 中使用 Sta…

Redis高级篇之缓存一致性详细教程

文章目录 0 前言1.缓存双写一致性的理解1.1 缓存按照操作来分 2. 数据库和缓存一致性的几种更新策略2.1 可以停机的情况2.2 我们讨论4种更新策略2.3 解决方案 总结 0 前言 缓存一致性问题在工作中绝对没办法回避的问题&#xff0c;比如&#xff1a;在实际开发过程中&#xff0c…

C++_day2

目录 1. 引用 reference&#xff08;重点&#xff09; 1.1 基础使用 1.2 特性 1.3 引用参数 2. C窄化&#xff08;了解&#xff09; 3. 输入&#xff08;熟悉&#xff09; 4. string 字符串类&#xff08;掌握&#xff09; 4.1 基础使用 4.2 取出元素 4.3 字符串与数字转换 5. …

Vuex的基本使用

文章目录 一、Vuex概述1.是什么2.使用场景3.优势4.注意二、如何构建vuex多组件共享数据环境1.创建项目2.创建三个组件3.源代码三、vuex 的使用 - 创建仓库1.安装 vuex2.新建 `store/index.js` 专门存放 vuex3.创建仓库 `store/index.js`4 在 main.js 中导入挂载到 Vue 实例上5.…

WPF+MVVM案例实战(二十一)- 制作一个侧边弹窗栏(CD类)

文章目录 1、案例效果1、侧边栏分类2、CD类侧边弹窗实现1、样式代码实现2、功能代码实现3 运行效果4、源代码获取1、案例效果 1、侧边栏分类 A类 :左侧弹出侧边栏B类 :右侧弹出侧边栏C类 :顶部弹出侧边栏D类 :底部弹出侧边栏2、CD类侧边弹窗实现 1、样式代码实现 在原有的…

揭开广告引擎的神秘面纱:如何在0.1秒内精准匹配用户需求?

目录 一、广告系统与广告引擎介绍 &#xff08;一&#xff09;广告系统与广告粗分 &#xff08;二&#xff09;广告引擎在广告系统中的重要性分析 二、广告引擎整体架构和工作过程 &#xff08;一&#xff09;一般概述 &#xff08;二&#xff09;核心功能架构图 三、标…

[论文阅读]A Survey of Embodied Learning for Object-Centric Robotic Manipulation

Abstract --以对象为中心的机器人操纵的Embodied learning是体现人工智能中一个快速发展且具有挑战性的领域。它对于推进下一代智能机器人至关重要&#xff0c;最近引起了人们的极大兴趣。与数据驱动的机器学习方法不同&#xff0c;具身学习侧重于通过与环境的物理交互和感知反…

NFTScan Site:以蓝标认证与高级项目管理功能赋能 NFT 项目

自 NFTScan Site 上线以来&#xff0c;它迅速成为 NFT 市场中的一支重要力量&#xff0c;凭借对各类 NFT 集合、市场以及 NFTfi 项目的认证获得了广泛认可。这个平台帮助许多项目提升了曝光度和可见性&#xff0c;为它们在竞争激烈的 NFT 市场中创造了更大的成功机会。 在最新更…

指数分布的原理和应用

本文介绍指数分布&#xff0c;及其推导原理。 Ref: 指数分布 开始之前&#xff0c;先看个概率密度函数的小问题&#xff1a; 问题描述&#xff1a;你于上午10点到达车站&#xff0c;车在10点到10:30 之间到达的时刻 X 的概率密度函数如图&#xff1a; 则使用分段积分&#xff0…

Javase——正则表达式

正则表达式的相关使用 public static void main(String[] args) {//校验QQ号 System.out.println("3602222222".matches("[1-9][0-9]{4,}"));// 校验18位身份证号 System.out.println("11050220240830901X".matches("^([0-9]){7,18}…

安装中文版 Matlab R2022a

下载安装包 压缩包有点大&#xff0c;大概20G 百度网盘&#xff1a;下载链接 提取码&#xff1a;rmja 安装 解压后打开目录&#xff0c;右键以管理员身份运行 setup.exe 选择输入安装秘钥 输入秘钥&#xff1a; 50874-33247-14209-37962-45495-25133-28159-33348-18070-6088…

SICTF Round #4|MISC

1.派森 腐乳昂木 奥普瑞特儿 阴坡尔特 艾克斯奥尔 腐乳昂木 提克有第爱慕 阴坡尔特 ⭐ 弗拉格 等于 布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉布拉 印刻 等于 左中括号右中括号 佛儿 唉 因 梯软者左括号 零&#xff0c;楞左括号弗拉格右…