【数据结构】树-二叉树-堆(下)


🍃 如果觉得本系列文章内容还不错,欢迎订阅🚩
🎊个人主页:小编的个人主页
🎀 🎉欢迎大家点赞👍收藏⭐文章
✌️ 🤞 🤟 🤘 🤙 👈 👉 👆 🖕 👇 ☝️ 👍

目录

  • 🐼前言
  • 🐼堆
  • 🐼初始化堆
  • 🐼堆的销毁
  • 🐼堆的插入
  • 🐼向上调整算法
  • 🐼堆的判空
  • 🐼 堆的删除
  • 🐼向下调整算法
  • 🐼取堆顶的数据
  • 🐼堆的数据个数
  • 🐼全部源码
  • 🐼文末

🐼前言

🌟在上一节我们介绍了二叉树的相关概念,如果感兴趣的小伙伴,可以阅读我的上一篇文章:> 树-二叉树-堆,这一节我们介绍一种特殊的二叉树:

🐼堆

堆的概念
一般使用顺序结构的二叉树称之为堆,堆是一种特殊的二叉树,不仅具有二叉树的特性,还具有其他的特性。
😃如果有⼀个关键码的集合 K = {k0 , k1 , k2 , …,kn−1 } ,把它的所有元素按完全⼆叉树的顺序存储方式存储,在⼀个⼀维数组中,并满足: Ki <= K2∗i+1 ( Ki >=K2∗i+1 且 Ki <= K2∗i+2 ),i = 0、1、2… ,则称为堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

🐋小根堆
在这里插入图片描述🐋大根堆
在这里插入图片描述

🐾现在我们来讨论二叉树的性质:
对于具有 n 个结点的完全二叉树,如果按照从上到下从左到右的数组顺序对所有结点从0 开始编号,则对于序号为 i 的结点有:
如果i>0,那么双亲节点序号: (i-1)/2 ;
i=0,为根节点,无双亲节点。
如果2i+1<n;左孩子节点: 2i+1
如果2i+2<n右孩子节点:2i+2

✨我们既然知道堆是基于数组来实现的。那和我们之前实现的顺序表,栈一样:现在我们来定义堆的结构:

typedef	int HPDataType;
typedef struct Heap
{HPDataType* arr;int size;int capacity;
}Hp;

以下是堆常见方法的实现以及解析:

🐼初始化堆

//初始化堆
void HeapInit(Hp* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}

🌻代码解析

✨ 我们将还未开辟的数组指针置为空,并将堆中有效元素个数和容量大小置为0。
时间复杂度为O(1),程序执行常数次,空间复杂度为O(1),只创建有效个变量

🐼堆的销毁

// 堆的销毁
void HeapDestory(Hp* php)
{assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}

🌻代码解析

✨ 如果指向堆数组的空间存在,释放指向数组的那块空间,并将其置为NULL,防止野指针的发生。最后重置size和capacity
时间复杂度为O(1),程序执行常数次,空间复杂度为O(1),只创建有效个变量

🐼堆的插入

/ 堆的插入
void HeapPush(Hp* php, HPDataType x)
{assert(php);//检查是否需要增容if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->arr, newcapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;//尾部插入.向上调整AdjustUp(php->arr, php->size);++php->size;
}

🌻代码解析

✨ 向堆内插入元素默认是在尾部插入元素。首先要检查堆是否为空,如果为空,那么就要通过realloc进行增容操作,并更新capacity。将要插入的元素插入。因为是堆结构,每插入一个元素就需要对堆进行重新排序。所以这里封装了一个AdjustUp函数,这个函数用于调整堆值的关系,目的是,如果插入一个元素,是大根堆,结果还是大根堆,是小根堆,结果还是小根堆。
🍀测试结果:
在这里插入图片描述

下面来探讨向上调整算法的实现。

🐼向上调整算法

//向上调整
void AdjustUp(HPDataType* arr, int child)
{int parent = (child - 1) / 2;//建小堆while (child > 0){//<小堆//>大堆if (arr[child] < arr[parent] ){Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

🌻代码解析

✨ 我们调整参数的是堆数组,所以用arr接收,以及插入的孩子节点。
在上面二叉树的性质我们得知,已知孩子节点,则父节点是:parent = (child-1)/2,这里我们如果建小堆,即孩子节点要大于双亲节点的,不满足,则进行交换,并接着让child走到parent,parent再走到parent的父节点。如果已经满足,则跳出循坏。
🍂画图剖析:
在这里插入图片描述
如上图,每次在堆尾节点插入元素,需要向上调整,重新建堆

🚗这里来详细说明向上调整算法的时间复杂度:
因为堆是完全二叉树,这里近似看做满二叉树,对时间复杂度没有影响。
分析:
第1层, 2^0 个结点,需要向上移动0层.
第2层, 2^1 个结点,需要向上移动1层.
第3层, 2^2 个结点,需要向上移动2层.
第4层, 2^3 个结点,需要向上移动3层.

第h层, 2^(h-1) 个结点,需要向上移动h-1层.
则需要移动结点总的移动步数为:每层结点个数 * 向上调整次数(第⼀层调整次数为0)
在这里插入图片描述

🐼堆的判空

// 堆的判空
bool HeapEmpty(Hp* php)
{assert(php);return php->size == 0;
}

🌻代码解析

✨ 通过堆中有效元素的个数直接返回布尔值。
时间复杂度为O(1),程序执行常数次,空间复杂度为O(1),只创建有效个变量

🐼 堆的删除

void HeapPop(Hp* php)
{assert(!HeapEmpty(php));//交换第一个和最后一个元素Swap(&php->arr[0], &php->arr[php->size - 1]);php->size--;//向下调整AdjustDown(php->arr, 0,php->size);
}

🌻代码解析

✨ 删除堆中的数据,指的是删除堆头的数据。删除堆头,不能直接将堆头删除,这样就破坏了堆结构,而是通过将要删除的第一个元素和最后一个元素互相交换位置。不过这样又会打乱堆,此时我们有需要再次调整堆,将堆调整成有序堆。

下面我们介绍令一种调整算法:向下调整算法。

🐼向下调整算法

//向下调整
void AdjustDown(HPDataType* arr, int parent, int n)
{HPDataType child = parent * 2 + 1;while (child < n){//找最小的孩子--小堆//找最大的孩子--大堆if (arr[child] < arr[child + 1] && child+1<n){child++;}//<小堆//>大堆if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

🌻代码解析

✨ 首先,向下调整的还是堆数组,传过来的初始都是根节点,堆中有效元素个数是n。
📝向下调整算法:
将堆顶元素与堆中最后一个元素进行交换
• 删除堆中最后⼀个元素
• 将堆顶元素向下调整到满足堆特性为止。
🍏传过来的是父节点,所以先找到孩子节点child。我们上面已知双亲节点,求孩子节点:child =parent * 2 + 1; 。这里如果建堆,要先找到孩子节点中较小的孩子(child),再与双亲节点(parent)交换,注意,右孩子(child)也要小于节点个数,接着,如果较小的孩子(child)小于双亲节点(parent),交换较小的孩子(child)和双亲节点(parent)保存的值,再让双亲节点(parent)走到孩子节点(child),再更新孩子节点,否则,直接,跳出循环。child最多不能超过有效节点个数n作为循环条件.

🍂画图剖析:
在这里插入图片描述

🚗这里来详细说明向下调整算法的时间复杂度
分析:
第1层, 2^0 个结点,需要向下移动h-1层.
第2层, 2^1 个结点,需要向下移动h-2层.
第3层, 2^2 个结点,需要向下移动h-3层.
第4层, 2^3 个结点,需要向下移动h-4层.

第h-1层, 2^(h-2) 个结点,需要向下移动1层.
则需要移动结点总的移动步数为:每层结点个数 * 向上调整次数(第⼀层调整次数为0)
在这里插入图片描述
总结,堆的向上调整算法和向下调整算法都既可以建大堆也可以建小堆。向上调整算法主要是堆的插入。而向下调整算法用于建堆和堆排序。这里能看出,向下调整算法时间复杂度比向上调整算法更优

测试结果同下面取堆顶的数据一起展示。

🐼取堆顶的数据

// 取堆顶的数据
HPDataType HeapTop(Hp* php)
{assert(!HeapEmpty(php));return php->arr[0];
}

🌻代码解析
✨ 堆不为空才可以取元素。将堆顶元素返回。
时间复杂度为O(1),程序执行常数次,空间复杂度为O(1),只创建有效个变量
🍀测试结果:
在这里插入图片描述

🐼堆的数据个数

int HeapSize(Hp* php)
{assert(php);return php->size;
}

🌻代码解析

✨直接返回堆中有效元素个数
时间复杂度为O(1),程序执行常数次,空间复杂度为O(1),只创建有效个变量

🐼全部源码

Heap.h

#define  _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef	int HPDataType;typedef struct Heap
{HPDataType* arr;int size;int capacity;
}Hp;//初始化堆
void HeapInit(Hp* php);
// 堆的销毁
void HeapDestory(Hp* php);
// 堆的插入//尾部插入
void HeapPush(Hp* php, HPDataType x);
// 堆的删除//头部删除
void HeapPop(Hp* php);
// 取堆顶的数据
HPDataType HeapTop(Hp* php);
// 堆的数据个数
int HeapSize(Hp* php);
// 堆的判空
bool HeapEmpty(Hp* php);
//交换
void Swap(HPDataType* x, HPDataType* y);//向下调整
void AdjustDown(HPDataType* arr, int parent, int n);
//向上调整
void AdjustUp(HPDataType* arr, int child);

Heap.c

#include "Heap.h"//初始化堆
void HeapInit(Hp* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}// 堆的销毁
void HeapDestory(Hp* php)
{assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}void Swap(HPDataType* x, HPDataType* y)
{HPDataType tmp = *x;*x = *y;*y = tmp;
}
//向上调整
void AdjustUp(HPDataType* arr, int child)
{int parent = (child - 1) / 2;//建小堆while (child > 0){//<小堆//>大堆if (arr[child] < arr[parent] ){Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}//向下调整
void AdjustDown(HPDataType* arr, int parent, int n)
{HPDataType child = parent * 2 + 1;while (child < n){//找最小的孩子--小堆//找最大的孩子--大堆if (arr[child] < arr[child + 1] && child+1<n){child++;}//<小堆//>大堆if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}// 堆的插入
void HeapPush(Hp* php, HPDataType x)
{assert(php);//检查是否需要增容if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->arr, newcapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;//尾部插入.向上调整AdjustUp(php->arr, php->size);++php->size;
}// 堆的判空
bool HeapEmpty(Hp* php)
{assert(php);return php->size == 0;
}
// 堆的删除
void HeapPop(Hp* php)
{assert(!HeapEmpty(php));//交换第一个和最后一个元素Swap(&php->arr[0], &php->arr[php->size - 1]);php->size--;//向下调整AdjustDown(php->arr, 0,php->size);
}// 取堆顶的数据
HPDataType HeapTop(Hp* php)
{assert(!HeapEmpty(php));return php->arr[0];
}
// 堆的数据个数
int HeapSize(Hp* php)
{assert(php);return php->size;
}

test.c

void test()
{Hp hp;HeapInit(&hp);//堆的插入HeapPush(&hp, 8);HeapPush(&hp, 14);HeapPush(&hp, 6);HeapPush(&hp, 17);//堆的删除/*HeapPop(&hp);HeapPop(&hp);HeapPop(&hp);HeapPop(&hp);*/while (!HeapEmpty(&hp)){printf("%d ", HeapTop(&hp));HeapPop(&hp);}HeapDestory(&hp);
}

🐼文末

感谢你看到这里,如果觉得本篇文章对你有帮助,点个赞👍 吧,你的点赞就是我更新的最大动力 ⛅️🌈 ☀️

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

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

相关文章

-XSS-

链接 https://github.com/do0dl3/xss-labs 搭建过程非常容易的 搭建好之后&#xff0c;就可以点击图片开始闯关了 第一关--JS弹窗函数alert() 显示payload的长度是4 level1.php?nametest level1.php?nametest1 发现只要改变name的值就显示什么在页面上 没有什么过滤的 …

【数据结构与算法】《Java 算法宝典:探秘从排序到回溯的奇妙世界》

目录 标题&#xff1a;《Java 算法宝典&#xff1a;探秘从排序到回溯的奇妙世界》一、排序算法1、冒泡排序2、选择排序3、插入排序4、快速排序5、归并排序 二、查找算法1、线性查找2、二分查找 三、递归算法四、动态规划五、图算法1. 深度优先搜索&#xff08;DFS&#xff09;2…

transformControls THREE.Object3D.add: object not an instance of THREE.Object3D.

把scene.add(transformControls);改为scene.add(transformControls.getHelper());

计算机视觉专栏(1)【LeNet】论文详解

Lenet 系列 论文精讲部分0.摘要1.引言2.CNN3.结果分析4.总结 论文精讲部分 本专栏旨在深入解析计算机视觉模型的论文及其发展背景&#xff0c;并通过代码部分的实际实验来加深理解。读者可以根据自己的需要参考其中的内容。其主体为原文&#xff0c;笔者理解内容会采用引用格式…

[ 问题解决篇 ] 解决windows虚拟机安装vmtools报错-winserver2012安装vmtools及安装KB2919355补丁 (附离线工具)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

Window on ARM解锁所有的TTS语音包供python调用

Window on ARM解锁所有的TTS语音包供python调用 可用的语音包查看查看TTS可用的语音包解锁语音包设置升级系统打开注册表导出注册表修改注册表导入新的注册表可用的语音包查看 微软的Windows 10操作系统为设备上安装的每种语言提供了一套语音。但只有部分已安装的语音能在整个…

EPLAN创建宏并自定义部件库详细案例操作(三)

#通过导入EDZ格式部件库的样式,模仿制作一个自定义的部件库# 续 EPLAN创建宏并自定义部件库详细案例操作(二) 需要部件库文件(EDZ)格式,可以在此下载: https://download.csdn.net/download/weixin_44166380/89935582 五、创建部件库 本章节的部分操作忽略,具体可参见…

电子电气架构 --- 车载芯片现状

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所有人的看法和评价都是暂时的&#xff0c;只有自己的经历是伴随一生的&#xff0c;几乎所有的担忧和畏惧…

我docker拉取mysql镜像时用的是latest,我该怎么看我的镜像版本是多少?可以通过一下三种方法查看

我docker拉取mysql镜像时用的是latest&#xff0c;我该怎么看我的镜像版本是多少&#xff1f; 方法一&#xff1a;查看 Docker 镜像标签 你可以查看 Docker 镜像的标签信息&#xff0c;了解当前使用的 MySQL 镜像版本。 具体步骤如下&#xff1a; 1. 列出本地 Docker 镜像&am…

识别风险的提示清单

在PMBOK&#xff08;《项目管理体系指南》&#xff09;中&#xff0c;介绍了这样一个概念&#xff0c;在识别风险时&#xff0c;可以提供一个参考清单&#xff0c;让大家基于一个清单来思考&#xff0c;这个项目可能有哪些具体的风险。 PMBOK中的风险提示清单&#xff0c;包括…

Unreal Engine 5 C++(C#)开发:使用蓝图库实现插件(二)编辑BPLibrary.h中的枚举及结构体

目录 引言 一、头文件编写 1.1Kismet/BlueprintFunctionLibrary.h 1.2BPLibrary.generated.h的作用 1.3IImageWrapper.h 1.4 IImageWrapperModule.h 1.5 Engine/Texture2D.h 1.6CoreMinimal.h 二、定义图片/路径类型的枚举 2.1图片枚举类EImageType 2.2路径枚举类EPath…

Vuestic 整理使用

简单示例 1. 条件渲染 2. 列表渲染 3. 组件插槽 4. 插值语法 5. 前后端路由的区别(还是转一下,可以减少代码量)SFC 构建 … … Okay&#xff0c;可以干活了&#xff0c;通顺 数据表的操作更加简化了 数据类别通过后端路由区别,但是还得由前端路由转一下 简单了许多呀,上脚手…

ssm017网上花店设计+vue(论文+源码)_kaic

设计题目&#xff1a;网上花店的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信…

网页上视频没有提供下载权限怎么办?

以腾讯会议录屏没有提供下载权限为例&#xff0c;该怎么办呢&#xff1f; 最好的办法就是找到管理员&#xff0c;开启下载权限。如果找不到呢&#xff0c;那就用这个办法下载。 1.打开Microsoft Edge浏览器的扩展 2.搜索“视频下载”&#xff0c;选择“视频下载Pro” 3.点击“…

使用pyecharts绘制词云图

一、什么是词云图&#xff1f; 词云图是一种用来展现高频关键词的可视化表达&#xff0c;通过文字、色彩、图形的搭配&#xff0c;产生有冲击力地视觉效果&#xff0c;而且能够传达有价值的信息。 制作词云图的网站有很多&#xff0c;简单方便&#xff0c;适合小批量操作。 …

一、ARMv8寄存器之通用、状态、特殊寄存器

ARMV8核心寄存器数量是非常大的&#xff0c;为了更好的学习&#xff0c;可以划分为以下几大类&#xff1a; 通用寄存器。这类寄存器主要是用来暂存数据和参与运算。通过load\store指令操作。状态寄存器。AArch64体系结构使用PSTATE寄存器表示当前处理器状态。特殊寄存器。有专门…

云渲染突破酒店3D动画渲染速度与成本瓶颈!

3D动画已经成为众多行业&#xff0c;尤其是酒店业&#xff0c;用于营销和展示其独特卖点的重要工具。通过生动的3D动画&#xff0c;酒店能够突出其特色和优势&#xff0c;从而吸引更多潜在客户。然而&#xff0c;在3D动画制作过程中&#xff0c;渲染环节往往是一个耗时且技术要…

LabVIEW偏振调制激光高精度测距系统

在航空航天、汽车制造、桥梁建筑等先进制造领域&#xff0c;许多大型零件的装配精度要求越来越高&#xff0c;传统的测距方法在面对大尺寸、高精度测量时&#xff0c;难以满足工业应用的要求。绝对测距技术在大尺度测量上往往会因受环境影响大、测距精度低而无法满足需求。基于…

WPF+MVVM案例实战(六)- 自定义分页控件实现

文章目录 1、项目准备2、功能实现1、分页控件 DataPager 实现2、分页控件数据模型与查询行为3、数据界面实现 3、运行效果4、源代码获取 1、项目准备 打开项目 Wpf_Examples&#xff0c;新建 PageBarWindow.xaml 界面、PageBarViewModel.cs ,在用户控件库 UserControlLib中创建…

WPF+MVVM案例实战(十一)- 环形进度条实现

文章目录 1、运行效果2、功能实现1、文件创建与代码实现2、角度转换器实现3、命名空间引用3、源代码下载1、运行效果 2、功能实现 1、文件创建与代码实现 打开 Wpf_Examples 项目,在Views 文件夹下创建 CircularProgressBar.xaml 窗体文件。 CircularProgressBar.xaml 代码实…