当前位置: 首页 > news >正文

二叉树第二期:堆的实现与应用

若对树与二叉树的相关概念,不太熟悉的同学,可移置上一期博客

链接:二叉树第一期:树与二叉树的概念-CSDN博客

本博客目标:对二叉树的顺序结构,进行深入且具体的讲解,同时学习二叉树顺序结构的应用——数据结构:堆的实现,以及堆的应用:如堆排序,又或者TOP-K问题;

感谢移置残风的主页:残风也想永存-CSDN博客,❤❤❤

一、堆的定义

  • 堆的定义:堆是一颗完全二叉树,且所有的父亲结点与子结点有相同的大小关系。
  • 大堆:所有的父亲结点的值 都比 子结点要
  • :所有的父亲结点的值 都比 子结点要

        上期,我们讲过,对于完全二叉树,若用数组的下标0,1,2...,从左到右依次表示第一层,第二层...,则父亲结点与子结点的关系,可用下标表示出来。

        而堆本身就是一种特殊的完全二叉树,所以用顺序结构表示堆,再简单不过~

二、堆的实现讲解

//堆的结构体声明与定义

typedef int HpDateType;

typedef struct Heap
{
    HpDateType* date;
    size_t size;
    size_t capacity;
}Heap;

//堆的函数接口声明:

void HeapInit(Heap* php);//初始化
void HeapDestory(Heap* php);//销毁
void HeapPush(Heap* php, HpDateType x);//插入 
void HeapPop(Heap* php);//删除堆顶的元素
HpDateType HeapTop(Heap* php);//返回堆顶元素
size_t HeapSize(Heap* php);//返回堆的数据个数
bool HeapEmpty(Heap* php);//判空

//以下为上面函数接口的子函数,其目的是插入或

//删除元素后,符合堆的定义——但因其重要性~,下面会着重讲解

void AdjustUp(HpDateType* a, int n);//向上调整算法
void AdjustDown(HpDateType* a, int n, int size);向下调整算法

        通过上面的声明,可以清楚的发现,其堆的实现,和动态顺序表的实现,极为相似;但又有一些区别; 比如在插入数据的时候,我们并不只是在最后一个数据的后面插入一个数据就行了,而是要通过向上调整算法,保持其符合堆的定义;在删除数据的时候,我们也不是删除最后一个数据,而是删除堆顶的元素。

1.向上调整算法
i.实现思路讲解

        应用效果:给你一个堆,在尾部任意插入元素,将该结点调整到适合他的位置,大堆,比父亲小;若是小堆,比父亲小。  

         (建大堆)将插入得新结点,与其父亲做比较,若比父亲大,则交换数据,进行下一次循环,若比父亲小,或该结点的下标到0位置,则调整完毕,循环结束。 

ii.复杂度

        向上调整算法:最坏的情况,为调整高度次,假设二叉树的有N个结点,所以时间复杂度为O(logN)

iii.代码
void AdjustUp(HpDateType* a, int n)
{assert(a);int child = n;int father = (child - 1) / 2;while (child > 0){// 大堆if (a[child] > a[father]){Swap(&a[father], &a[child]);child = father;father = (child - 1) / 2;}else break;}
}
2.向下调整算法
i.实现思路讲解

        向下调整算法使用前提:左右子树是相同的堆,若是建小堆,左右子树都是小堆,才可使用向下调整算法;

ii.时间复杂度推导

        向下调整算法:最坏的情况,为调整高度次,假设二叉树的有N个结点,所以时间复杂度为O(logN)

iii.代码
void AdjustDown(HpDateType* a, int n, int size)
{assert(a);int father = n;int child = 2 * father + 1;while (2 * father + 1 < size){// 左孩子比父亲大的假设不成立if (child + 1 < size && a[child] < a[child + 1]){child += 1;}// 大堆if (a[child] > a[father]){Swap(&a[child], &a[father]);father = child;child = 2 * father + 1;}elsebreak;}
}
3.插入元素

         在插入数据的时候,我们并不只是在最后一个数据的后面插入一个数据就行了,而是要通过向上调整算法,保持其符合堆的定义;

void HeapPush(Heap* php, HpDateType x)
{assert(php);//查容 & 扩容 if (php->size == php->capacity){size_t newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HpDateType* tmp = (HpDateType*)realloc(php->date, newcapacity * sizeof(HpDateType));if (!tmp){perror("realloc mistake");exit(-1);}php->date = tmp;php->capacity = newcapacity;}//插入php->date[php->size++] = x;//向上调整堆;AdjustUp(php->date, php->size - 1);
}
4.删除堆顶元素

        在删除数据的时候,我们也不是删除最后一个数据,而是删除堆顶的元素。且要通过向下调整算法,保持其符合堆的定义;

void HeapPop(Heap* php)
{assert(php);assert(php->size > 0);//踹走堆顶元素;Swap(&php->date[0], &php->date[php->size-1]);php->size--;//向下调整堆AdjustDown(php->date, 0, php->size);
}

三、实现堆的源码

1.Heap.h
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include<time.h>
#include<assert.h>typedef int HpDateType;typedef struct Heap
{HpDateType* date;size_t size;size_t capacity;
}Heap;void HeapInit(Heap* php);//初始化
void HeapDestory(Heap* php);//销毁
void HeapPush(Heap* php, HpDateType x);//插入 
void HeapPop(Heap* php);//删除 
HpDateType HeapTop(Heap* php);//返回堆顶元素
size_t HeapSize(Heap* php);//返回堆的数据个数
bool HeapEmpty(Heap* php);//判空void AdjustUp(HpDateType* a, int n);
void AdjustDown(HpDateType* a, int n, int size);
2.Heap.c
#define _CRT_SECURE_NO_WARNINGS 1#include"Heap.h"void HeapInit(Heap* php)
{assert(php);php->date = NULL;php->size = php->capacity = 0;
}void HeapDestory(Heap* php)
{assert(php);free(php->date);php->date = NULL;php->size = php->capacity = 0;
}void Swap(HpDateType* e1, HpDateType* e2)
{int tmp = *e1;*e1 = *e2;*e2 = tmp;
}void AdjustUp(HpDateType* a, int n)
{assert(a);int child = n;int father = (child - 1) / 2;while (child > 0){// 小堆if (a[child] < a[father]){Swap(&a[father], &a[child]);child = father;father = (child - 1) / 2;}else {break;}	}
}void HeapPush(Heap* php, HpDateType x)
{assert(php);//查容 & 扩容 if (php->size == php->capacity){size_t newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HpDateType* tmp = (HpDateType*)realloc(php->date, newcapacity * sizeof(HpDateType));if (!tmp){perror("realloc mistake");exit(-1);}php->date = tmp;php->capacity = newcapacity;}//插入php->date[php->size++] = x;//向上调整堆;AdjustUp(php->date, php->size - 1);
}void AdjustDown(HpDateType* a, int n, int size)
{assert(a);int father = n;int child = 2 * father + 1;while (2 * father + 1 < size){// 左孩子比父亲小的假设不成立if (child + 1 < size && a[child] > a[child + 1]){child += 1;}// 小堆if (a[child] < a[father]){Swap(&a[child], &a[father]);father = child;child = 2 * father + 1;}else{break;}}
}void HeapPop(Heap* php)
{assert(php);assert(php->size > 0);//踹走堆顶元素;Swap(&php->date[0], &php->date[php->size-1]);php->size--;//向下调整堆AdjustDown(php->date, 0, php->size);
}HpDateType HeapTop(Heap* php)
{assert(php);return php->date[0];
}size_t HeapSize(Heap* php)
{assert(php);return php->size;
}
bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}

四、堆的应用 

1.建堆
i.向上调整建堆(时间复杂度推导)

        问题:给你一个数组,返回一个大堆;

        问起建堆,可能你们会说,创建一个堆的数据结构,然后不断的Push就行了;可是空间复杂度却是O(N),我们如何在空间复杂度O(1)的情况下,建一个堆呢?

        HeapPush的时候,是在堆尾插入一个数据,然后向上调整,而我们其实可以省去插入的过程,在给定数组的上面,只使用向上调整算法,实现建堆;

        省去了开辟空间的消耗,空间复杂度为O(1);时间复杂度为O(NlogN)

 

ii. 向下调整建堆(时间复杂度推导)

        向上调整建堆的时间复杂度为O(NlogN),若面试官说O(NlogN),不好,问你能否将时间复杂度?你是否会觉得不可思议?而你又会如何解决呢?我们大脑的思维很难凭空创造,但我们可以从已有的问题,得到启发;下面我们重新分析一下向上调整建堆时间浪费在何处,

        我们会发现,层数越高结点,最坏调整次数越高,与此同时,结点个数也越多,我说这可能是问题的突破高,我们如何让调整次数多的结点,个数减少呢?我们会想到向下调整算法,向下调整算法,有个使用前提:左右子树是相同的堆,才可使用向下调整算法。所以我们可以从最后一个非叶子结点往前调整;如上图,先向下调整28结点,依次往前13、56、32、...、到最后的根结点。       

        优点:结点个数越多的那一层,向下调整次数反而越少;时间复杂度为O(N)

                

相关文章:

二叉树第二期:堆的实现与应用

若对树与二叉树的相关概念&#xff0c;不太熟悉的同学&#xff0c;可移置上一期博客 链接&#xff1a;二叉树第一期&#xff1a;树与二叉树的概念-CSDN博客 本博客目标&#xff1a;对二叉树的顺序结构&#xff0c;进行深入且具体的讲解&#xff0c;同时学习二叉树顺序结构的应用…...

python-求出 e 的值

[题目描述] 利用公式 e11/1!1/2!1/3!⋯1/&#x1d45b;!&#xff0c;求 e 的值&#xff0c;要求保留小数点后 10 位。输入&#xff1a; 输入只有一行&#xff0c;该行包含一个整数 n&#xff0c;表示计算 e 时累加到1/n!。输出&#xff1a; 输出只有一行&#xff0c;该行包含计…...

模型微调方法

文章目录 LoRADoRAMoRA 以下部分参考自: https://mp.weixin.qq.com/s/OxYNpXcyHF57OShQC26n4g LoRA LoRA是微软于2021年推出的一种经济型微调模型参数的方法。 它在冻结大部分的模型参数的情况下&#xff0c;仅仅更新额外的部分参数。其性能与全参数微调相似。 LoRA假设微调期间…...

cesium使用cesium-navigation-es6插件创建指南针比例尺

cesium-navigation-es6 是一个为 Cesium.js 提供导航控件的库&#xff0c;它提供了一些常见的用户界面组件&#xff0c;用于在 Cesium 场景中实现用户导航和交互。下面将介绍如何在项目中使用 cesium-navigation-es6。 使用步骤 1. 安装 cesium-navigation-es6 首先&#xf…...

go sync包(七)Sync.Map

Sync.Map 原理 通过 read 和 dirty 两个字段实现数据的读写分离&#xff0c;读的数据存在只读字段 read 上&#xff0c;将最新写入的数据存在 dirty 字段上。读取时会先查询 read&#xff0c;不存在再查询 dirty&#xff0c;写入时则只写入 dirty。读取 read 并不需要加锁&am…...

Batch文件中的goto命令:控制流程的艺术

Batch文件&#xff0c;也称为批处理脚本&#xff0c;是Windows操作系统中用于自动化任务的一种脚本文件。在Batch脚本中&#xff0c;goto命令是一个至关重要的控制结构&#xff0c;它允许脚本跳转到指定的标签位置&#xff0c;从而实现循环、条件分支等复杂的控制流程。本文将详…...

【chatgpt】两层gcn提取最后一层节点输出特征,如何自定义简单数据集

文章目录 两层gcn&#xff0c;提取最后一层节点输出特征&#xff0c;10个节点&#xff0c;每个节点8个特征&#xff0c;连接关系随机生成&#xff08;无全连接层&#xff09;如何计算MSE 100个样本&#xff0c;并且使用批量大小为32进行训练第一个版本定义数据集出错&#xff0…...

Java面试题:讨论你如何保持对Java生态系统中新技术的了解

保持对Java生态系统中新技术的了解可以通过以下几种方法&#xff1a; 官方资源&#xff1a; Oracle的官方博客和新闻&#xff1a;Oracle是Java的主要维护者&#xff0c;其官方网站和博客会定期发布Java的新版本、功能更新和最佳实践。Java SE Documentation&#xff1a;Java官方…...

深度学习之Transformer模型的Vision Transformer(ViT)和Swin Transformer

Transformer 模型最初由 Vaswani 等人在 2017 年提出,是一种基于自注意力机制的深度学习模型。它在自然语言处理(NLP)领域取得了巨大成功,并且也逐渐被应用到计算机视觉任务中。以下是两种在计算机视觉领域中非常重要的 Transformer 模型:Vision Transformer(ViT)和 Swi…...

玩个游戏 找以下2个wordpress外贸主题的不同 你几找到几处

Aitken艾特肯wordpress外贸主题 适合中国产品出海的蓝色风格wordpress外贸主题&#xff0c;产品多图展示、可自定义显示产品详细参数。 https://www.jianzhanpress.com/?p7060 Ultra奥创工业装备公司wordpress主题 蓝色风格wordpress主题&#xff0c;适合装备制造、工业设备…...

React Native优质开源项目推荐与解析

目录 2. React Native的优势 2.1. 跨平台开发 2.2. 热更新 2.3. 丰富的社区资源 2.4. 优秀的性能 3. 优质开源项目推荐 3.1. React Navigation 3.1.1 项目简介 3.1.2 特点和优势 3.1.3 应用场景 3.2. Redux 3.2.1 项目简介 3.2.2 特点和优势 3.2.3 应用场景 3.3…...

树莓派安装windows系统

第1步&#xff1a; https://uupdump.net/下载对应的系统文件&#xff0c;所有选择项都默认选择。 第2步&#xff1a; 解压下载后的文件&#xff0c;双击运行下面文件。等待下载完成&#xff0c;等待过程很漫长&#xff0c;很考验耐心。 第3步&#xff1a; 提示已经finish了&…...

CSS-position/transform

1 需求 2 语法 在CSS中&#xff0c;positioning 和 transform 是两个非常重要的概念&#xff0c;它们分别用于控制元素在页面上的布局和变换。 Positioning CSS中的position属性用于设置元素的定位类型。它有几个值&#xff0c;包括&#xff1a; static&#xff1a;这是默认…...

面试题之一

路由的两种模式&#xff1a;hash模式和 history模式。 两种的区别、如何实现。 hash模式中#的作用 vue性能优化。具体如何实现&#xff08;回答了一个可以函数引入的方法引入路由。问&#xff09; keep-alive 说一下EventBus CSS&#xff1a; flex布局 css新特性 盒子模型 J…...

494. 目标和 Medium

给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可以在 2 之前添加 &#xff0c;在 1 之前添加 - &#xff0c;然…...

如何实现灌区闸门控制自动化?宏电“灌区哨兵”为灌区闸门控制添“智慧”动能

闸门控制站是节水灌溉工程中的重要组成部分。随着科技的不断进步和农田水利现代化的发展&#xff0c;传统的闸门控制和管理手段已经不能满足现代农业的发展要求。以宏电“灌区哨兵”为核心的闸门自动化控制系统&#xff0c;能有效解决灌区闸门距离远、数量多、不易操作、不好监…...

PHP电商系统开发指南数据库管理

回答&#xff1a;数据库管理是电商系统开发的关键&#xff0c;涉及数据的存储、管理和检索。选择合适的数据库引擎&#xff0c;如mysql或 postgresql。创建数据库架构&#xff0c;定义数据的组织方式&#xff08;如产品表、订单表&#xff09;。进行数据建模&#xff0c;考虑实…...

基于Vue.js的电商前端模板:Vue-Dashboard-Template的设计与实现

摘要 随着电子商务的飞速发展&#xff0c;前端页面的设计和实现变得愈发重要。本文介绍了一个基于Vue.js的电商前端模板——Vue-Dashboard-Template&#xff0c;旨在提供一个高性能、易扩展的电商平台前端解决方案。该模板遵循响应式设计、模块化、组件化开发等设计原则&#…...

论文解读:【CVPR2024】DUSt3R: Geometric 3D Vision Made Easy

论文“”https://openaccess.thecvf.com/content/CVPR2024/papers/Wang_DUSt3R_Geometric_3D_Vision_Made_Easy_CVPR_2024_paper.pdf 代码&#xff1a;GitHub - naver/dust3r: DUSt3R: Geometric 3D Vision Made Easy DUSt3R是一种旨在简化几何3D视觉任务的新框架。作者着重于…...

springboot助农电商系统-计算机毕业设计源码08655

摘要 近年来&#xff0c;电子商务的快速发展引起了行业和学术界的高度关注。基于移动端的助农电商系统旨在为用户提供一个简单、高效、便捷的农产品购物体验&#xff0c;它不仅要求用户清晰地查看所需信息&#xff0c;而且还要求界面设计精美&#xff0c;使得功能与页面完美融合…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧

上周三&#xff0c;HubSpot宣布已构建与ChatGPT的深度集成&#xff0c;这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋&#xff0c;但同时也存在一些关于数据安全的担忧。 许多网络声音声称&#xff0c;这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

SpringAI实战:ChatModel智能对话全解

一、引言&#xff1a;Spring AI 与 Chat Model 的核心价值 &#x1f680; 在 Java 生态中集成大模型能力&#xff0c;Spring AI 提供了高效的解决方案 &#x1f916;。其中 Chat Model 作为核心交互组件&#xff0c;通过标准化接口简化了与大语言模型&#xff08;LLM&#xff0…...