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

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

链接:二叉树第一期:树与二叉树的概念-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)

                

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

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

相关文章

[数据集][目标检测]桥梁检测数据集VOC+YOLO格式1116张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1116 标注数量(xml文件个数)&#xff1a;1116 标注数量(txt文件个数)&#xff1a;1116 标注…

《强化学习的数学原理》(2024春)_西湖大学赵世钰 Ch8 值函数拟合 【基于近似函数的 TD 算法:Sarsa、Q-leaning、DQN】

PPT 截取有用信息。 课程网站做习题。总体 MOOC 过一遍 1、学堂在线 视频 习题 2、相应章节 过电子书 复习 【下载&#xff1a; 本章 PDF GitHub 页面链接】 3、 MOOC 习题 跳过的 PDF 内容 学堂在线 课程页面链接 中国大学MOOC 课程页面链接 B 站 视频链接 PPT和书籍下载网址…

【原创实现 设计模式】Spring+策略+模版+工厂模式去掉if-else,实现开闭原则,优雅扩展

1 定义与优点 1.1 定义 策略模式&#xff08;Strategy Pattern&#xff09;属于对象的⾏为模式。他主要是用于针对同一个抽象行为&#xff0c;在程序运行时根据客户端不同的参数或者上下文&#xff0c;动态的选择不同的具体实现方式&#xff0c;即类的行为可以在运行时更改。…

MySQL1(初始数据库 概念 DDL建库建表 数据库的三大范式 表约束)

目录 一、初始数据库 二、概念 三、DDL建库建表 1. 数据库结构 2. SQL语句分类 3. DDL语句操作数据库 注释&#xff1a; 查看数据库&#xff1a; ​编辑创建数据库&#xff1a; 删除数据库&#xff1a; 选择数据库&#xff1a; 4. 数据库表的字段类型 4.1 字符串…

java将html转成图片

java 将html转成图片 1.导入jar2.代码3.展示结果4.注意事项 最近有一个需求需要根据指定的样式生成图片&#xff0c;使用java原生技术有些麻烦&#xff0c;所以上网搜了下案例&#xff0c;最后发现最好用的还是html2image&#xff0c;这里进行简单总结下。 1.导入jar <!-- 用…

腾讯视频VIP会员账号怎么扫码登录一个帐号登录几个人的设备?

腾讯视频VIP会员账号怎么扫码登录&#xff1f; 腾讯视频VIP会员账号要想实现扫码登录&#xff0c;仅支持在电脑Web网页版和WindowsPC软件上登录腾讯视频时&#xff0c;才可以实现手机QQ扫码登录腾讯视频。 腾讯视频VIP与SVIP会员有什么区别&#xff1f; 腾讯视频VIP会员&…

前端小白必学:对Cookie、localStorage 和 sessionStorage 的简单理解

前言 Cookie、localStorage 和 sessionStorage 作为Web开发领域中广泛采用的三种客户端数据存储技术&#xff0c;它们各自拥有独特的优势、应用场景及限制条件&#xff0c;共同支撑起前端数据管理的多样化需求。也是面试常考题之一&#xff0c;今天就和大家简单谈一下我对它们…

揭开大语言模型(LLM)内部运作的算法逻辑

本文探讨了 Anthropic 的突破性技术&#xff0c;以揭示大型语言模型 (LLM) 的内部工作原理&#xff0c;揭示其不透明的本质。通过深入研究LLM Claude Sonnet 的“大脑”&#xff0c;Anthropic 增强了人工智能的安全性和可解释性&#xff0c;为人工智能的决策过程提供了更深入的…

应用部署方式演变

应用部署方式演变 1.传统部署2.虚拟化部署3.容器化部署 1.传统部署 传统的应用程序部署是将多个应用程序直接部署在操作系统上&#xff0c;一旦其中的某个应用程序出现内存泄漏&#xff0c;那么该程序就会大量吞噬系统内容空间&#xff0c;导致其他应用程序无法正常运行。 2.虚…

如何让两个不同网段的直连地址通信(有点意思)

群里一个朋友出了个题&#xff1a;两个路由器接口直连&#xff0c;一个接口IP是1.1.1.1/30&#xff0c;一个接口IP是2.2.2.2/30&#xff0c;如何让它们通信&#xff1f; 群里的朋友们纷纷献策&#xff1a; 1、用PPP方式连接&#xff0c;直接通 2、配对端IP地址同网段的s…

鱼叉式钓鱼

鱼叉式网络钓鱼&#xff1a; 鱼叉式网络钓鱼是一种网络钓鱼形式&#xff0c;它针对特定个人或组织发送定制消息&#xff0c;旨在引发特定反应&#xff0c;例如泄露敏感信息或安装恶意软件。这些攻击高度个性化&#xff0c;使用从各种来源收集的信息&#xff0c;例如社交媒体资…

Face Adapter - 一键面部表情迁移、换脸工具 本地一键整合包下载

Face Adapter是一款高效的人脸编辑适配器&#xff0c;由浙江大学和腾讯联合开发&#xff0c;适用于预先训练的扩散模型&#xff0c;专门针对人脸再现和交换任务。 只需要上传一张源脸和一张参考人脸&#xff0c;就能按照参考人脸的风格生成相同的面部的表情&#xff0c;一键生…

掌握Python编程的深层技能

一、Python基础语法、变量、列表、字典等运用 1.运行python程序的两种方式 1.交互式即时得到程序的运行结果 2.脚本方式把程序写到文件里(约定俗称文件名后缀为.py),然后用python解释器解释执行其中的内容2.python程序运行的三个步骤 python3.8 C:\a\b\c.py 1.先启动python3…

Golang-channel理解

channel golang-channel语雀笔记整理 channelgolang channel的设计动机&#xff1f;chanel的数据结构/设计思考 golang channel的设计动机&#xff1f; channel是一种不同协程之间实现异步通信的数据结构。golang中有一种很经典的说法是要基于通信实现共享内存&#xff0c;而不…

机器学习基础:开源库学习-Numpy科学计算库

目录 Numpy科学计算库 什么是多维数组 数组基础 高维数组 操作和创建数组 Numpy介绍 创建数组 数组的属性 二维数组 三维数组 数组元素的数据类型 创建特殊的数组 np.arange() np.ones() np.zeros() np.eye() np.linspace() np.logspace() asarray() 数组运…

thymeleaf+mybatis(本文章用于期末考前10分钟速看)

期末速看 pom&#xff08;了解&#xff09;application.propertiessql代码Controller控制层视图service&#xff1a; 服务层mapper&#xff08;dao&#xff09;&#xff1a;持久层entity层(model层&#xff0c;domain层、 bean)&#xff1a;对应数据库表&#xff0c;实体类 效果…

谈谈你对AQS的理解

AQS概述 AQS&#xff0c;全称为AbstractQueuedSynchronizer&#xff0c;是Java并发包&#xff08;java.util.concurrent&#xff09;中一个核心的框架&#xff0c;主要用于构建阻塞式锁和相关的同步器&#xff0c;也是构建锁或者其他同步组件的基础框架。AQS提供了一种基于FIF…

模拟城市5: 未来之城 全DLC for Mac 下载安装包

模拟城市5&#xff1a;未来之城&#xff08;SimCity BuildIt&#xff09;是一款由Maxis开发并由 Electronic Arts&#xff08;EA&#xff09;发行的城市建设和管理模拟游戏。这款游戏最初在2014年发布&#xff0c;适用于iOS、Android以及Windows Phone平台&#xff0c;随后在20…

力扣最新详解5道题:两数之和三数之和四数之和

目录 一、查找总价格为目标值的两个商品 题目 题解 方法一&#xff1a;暴力枚举 方法二&#xff1a;对撞指针 二、两数之和 题目 题解 方法一&#xff1a;暴力枚举 方法二&#xff1a;哈希表法 三、三数之和 题目 题解 方法一&#xff1a;排序暴力枚举set去重 …

数据资产治理的智能化探索:结合云计算、大数据、人工智能等先进技术,探讨数据资产治理的智能化方法,为企业提供可靠、高效的数据资产解决方案,助力企业提升竞争力

一、引言 在信息化时代&#xff0c;数据已成为企业最重要的资产之一。随着云计算、大数据、人工智能等先进技术的飞速发展&#xff0c;数据资产治理面临着前所未有的机遇与挑战。本文旨在探讨如何结合这些先进技术&#xff0c;实现数据资产治理的智能化&#xff0c;为企业提供…