数组(完全二叉树)向下建堆法与堆排序O(N*logN)
TIPS
AdjustUp & AdjustDown
向上调整AdjustUp与向下调整AdjustDown的参数是一个数组(完全二叉树)+需要进行调整操作的数值的下标/一个数组(完全二叉树)+堆元素个数+需要调整操作的数值的下标。
实际上就是对完全二叉树当中的某一点进行调整直至在其局部范围内满足堆的性质。因此对于完全二叉树如果仅仅对若干个值进行AdjustUp/AdjustDown,并不会是这颗完全二叉树真正变成一个堆。
对于AdjustUp与AdjustDown,在没有任何限制与约束的情况之下,如果说你对数组(完全二叉树)中的某一个元素去进行调整的话,你可以这么做,只不过没有任何意义,因为他最终调整调整,直至在某一个局部范围内满足堆的性质。既然没有任何意义,那它的意义到底是什么呢?AdjustUp与AdjustDown的意义就在于建堆。
那如果说想使用这两个函数往建堆这条正确的方向去走,需要注意:
AdjustUp的前提必须是在child前面数据已经是一个堆;AdjustDown的前提必须是在parent左右两个子树必须是两个堆在这些前提之下,如果说你去AdjustUp与AdjustDown,那么是真正在朝着建堆前进。不然,你在怎么弄的话,只是在局部范围内进行兴风作浪,对于整体并不能使它变成一个堆。
HeapPush & HeapPop
HeapPush与HeapPop的本质与灵魂就是AdjustUp/AdjustDown。这两个的参数分别是一个堆结构体指针+一个需要往堆里面插入的值,一个堆结构体指针。
因此这两个函数的话需要在原先的数组(完全二叉树)就是一个堆的情况下才有意义,不然的话,如果说原先的数组并不是一个堆,那么插入一个数的操作与删除第一个元素意义不大。
数组(完全二叉树)建堆(向下建堆法)O(N)
我们讲过一个建堆方式,就是说是向上调整法建堆,实际上就是不断的插入,如果对于一个数据仅仅进行AdjustUp,只会最终使得局部范围内满足堆的性质,如果说我对数组(完全二叉树)当中的每一个数都进行AdjustUp,那么当我这个操作完了之后,这个数组(完全二叉树)我已经是一个堆了。那有没有其他的方式去建堆呢?
然后我们再进行向下调整法建堆的时候,显而易见的事实就是,我说你对叶子(就是说没有儿子的节点)进行向下调整,那还有什么意义?没有儿子,他调整的也跟白调一样。
所以说向下调整法的话,我需要从倒着往前走,这是第一点(这就为了使得AdjustDown朝着建堆的方向走
并且从倒着往前走之外,我还要从第一个非叶子节点开始进行向下调整。那我该怎么样去找到倒数第一个非叶子节点?我只要把最后一个元素的下标(child)这样((child-1)/2)找到他父亲不就万事大吉了吗?
为什么要倒着走呢?因为倒着走去向下调整的时候,我就可以确保目前这个节点的左右两个子树都已经是堆,这样子的话,我就确保AdjustDown的能够朝着正确的方向(建堆)前进,而不是一直在局部范围内在兴风作浪。
然后向下调整法建堆的话,它的效率比向上调整法建堆要高且差距极大。一般建堆的话是不会用向上调整建堆。


当然,要对数组(完全二叉树)建堆,首先必须得有两个万金油(AdjustUp 与 AdjustDown)
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void AdjustUp(int* a, int child)
{assert(a);int parent = (child - 1) / 2;while (child > 0){if (a[parent] < a[child]){Swap(a + parent, a + child);child = parent;parent = (child - 1) / 2;}else{break;}}
}
void AdjustDown(int* a, int parent, int n)
{assert(a);int child = 2 * parent + 1;while (child<n){if (child+1 <n && a[child] < a[child + 1]){child++;}if (a[parent] < a[child]){Swap(a + parent, a + child);parent = child;child = 2 * parent + 1;}else{break;}}
}演示一下向下调整法建堆
int main()
{int arr[15] = { 1,4,5,2,7,8,3,4,9,0,4,3,11,6,8 };int size = 15;for (int i = (size - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, i, size);}return 0;
}建堆的核心就在于把AdjustUp与AdjustDown的核心与意义彻底弄清楚
以后建堆就关注这两个: 数组(完全二叉树)得有+AdjustDown核心奥义
堆排序 O(NlogN)
堆在我们这边的优点就是说他已经不像之前那样单独的存储数据,他能够做到帮我们去选数
因此的话,从更加程度而言,它能够帮助我们排序,如果排序的数据量小的话倒还无所谓,我说排序的数据量一大,那么对于性能的要求就非常重要。与我们之前学过的冒泡排序,它的时间复杂度是O(N^2),对堆排序而言的话,它的时间复杂度是O(NlogN)。这两个差异极大
在堆排序之前的话,需要脑子拎清楚的是,我们现在已经不是在堆着那个结构体里面了,现在我们跟手撕数据结构的那个堆就是说自己手搓的那个堆无关了,现在的话相当于就是对题目中给我的数组(完全二叉树)直接进行建堆然后去进行排序。
首先建堆 [ O(N) 向下调整法 ] 数组得有+AdjustDown核心奥义
要对数组(完全二叉树)建堆如果你要升序排列,就要去建大根堆;如果你要降序排列,要去建小根堆,无论是大根还是小根,然后这个数组的顺序确定是从后往前依次慢慢确定下来的。
建堆的向下调整法前面已经讲了
其次调堆 [堆顶与数组末尾开始去倒] AdjustDown核心奥义
当把堆建完之后,接下来就是要去换数据了,这个过程当中的核心核心成员与利器就是AdjustDown,然后每次使用这个函数的时候都需要注意,当前这个堆的数据数量是多少,是每次都要减1减1的哟。
堆排序时间复杂度
堆排序的话,除了一开始要把原始数据(是数组或者说是完全二叉树)建堆之后,还要去转数据调堆。对于第一步,复杂度是O(N)。
然后调堆的时间复杂度也是logN*N,用错位相减法减减算算也很快的,或者说你用直观的方法去看一下,对于最后一层而言,如果说你想要确定最后一层(堆排序的话,它是确定的顺序是从数组的从后往前慢慢确定下来的),要确定最后一层的话,就需要不断的把堆顶的数据给往下调整,这就相当于就是说节点最多的层数,它需要调整的次数也是最多。
因此对于堆排序而言的话,就是说你在一开始对数组(完全二叉树)建堆的时候,无论是用什么方法去建堆,最后总的堆排序时间复杂度都是O(N*logN)
代码
int main()
{int arr[10] = { 1,4,3,5,7,9,8,6,2,0 };int size = 10;//建堆for (int i = (size - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, i, size);}//调堆int end = size - 1;while (end > 0){Swap(arr + 0, arr + end);AdjustDown(arr, 0, end);end--;}return 0;
}你会发现,在堆排序里面无论是建堆还是调堆,都与AdjustUp无关
相关文章:
数组(完全二叉树)向下建堆法与堆排序O(N*logN)
TIPS AdjustUp & AdjustDown向上调整AdjustUp与向下调整AdjustDown的参数是一个数组(完全二叉树)需要进行调整操作的数值的下标/一个数组(完全二叉树)堆元素个数需要调整操作的数值的下标。实际上就是对完全二叉树当中的某一点…...
Lua require 函数使用
从 Lua 的用户文档中我们知道 require("modName") 函数是用来加载模块的,而如果这个modName已经用require 加载过的,再调用require时,将直接返回模块的值。因为函数首先查找 package.loaded 表, 检测 modName 是否被加载…...
【面试】如何定位线上问题?
这个面试题我在两年社招的时候遇到过,前几天面试也遇到了。我觉得我每一次都答得中规中矩,今天来梳理复盘下,下次又被问到的时候希望可以答得更好。 下一次我应该会按照这个思路去答: 1、如果线上出现了问题,我们更多…...
字节二面,原来我对自动化测试的理解太浅了
如果你入职一家新的公司,领导让你开展自动化测试,作为一个新人,你肯定会手忙脚乱,你会如何落地自动化测试呢? 01 什么是自动化 有很多人做了很长时间的自动化但却连自动化的概念都不清楚,这样的人也是很悲…...
Android11.0 应用升级成功后立即断电重启,版本恢复
问题:客户反馈内置的应用升级成功后立刻断电重启,应用的版本被恢复。 使用adb命令升级客户应用,查看版本显示已更新,/data/system目录下packages.xml和packages.xml中应用版本信息均已更新 C:\Users\dell>adb shell dumpsys …...
关于python常用软件用法:Pycharm 常用功能
人生苦短,我用python 一.Pycharm的基本使用 1.在Pycharm下为你的Python项目配置Python解释器 (1).Setting>Project Interpreter>源码资料电子书:点击此处跳转文末名片获取 二.在Pycharm下创建Python文件、Python模块 1.File>New&g…...
SOLIDWORKS你不知道的小技巧
◉ SOLIDWORKS圆弧长度标注点智能标注,再选中该圆弧,然后分别点圆弧的两个端点,点击左键可以标注圆弧长度。◉ SOLIDWORKS强力裁剪剪裁实体中的强劲剪裁,除了可以裁剪实体外,还可以任意延伸实体。◉ SOLIDWORKS转折线转…...
有了HTTP,为啥还要用RPC
既然有 HTTP 请求,为什么还要用 RPC 调用? 一直以来都没有深究过RPC和HTTP的区别,不都是写一个服务然后在客户端调用么? HTTP和RPC最本质的区别,就是 RPC 主要是基于 TCP/IP 协议的,而 HTTP 服务主要是基…...
[leetcode] 动态规划
背包 先啃懂 背包九讲 01背包,即物品有限。 for 物品for 容量(倒序)P1048 [NOIP2005 普及组] 采药 [ 原题 | 题解 ] P1049 [NOIP2001 普及组] 装箱问题 [ 原题 | 题解 ] P1507 NASA的食物计划 [ 原题 | 题解 ] P1510 精卫填海 [ 原题 | 题…...
科大奥瑞物理实验——热电偶特性及其应用研究
实验名称:热电偶特性及其应用研究 1. 实验目的: 掌握电位差计的工作原理和结构特点;了解温差电偶测温的原理和方法;学会电位差计的使用及注意事项。 2. 实验器材: 电位差计 标准电池 光电检流计 稳压电源 温差电偶…...
Eclips快捷键大全(超详细)
Eclips快捷键大全(超详细)前言一、常用快捷键二、编辑快捷键三、导航快捷键四、运行和调试快捷键五、重构快捷键六、代码生成快捷键七、项目导航快捷键八、帮助快捷键九、搜索快捷键十、标记快捷键十一、版本控制快捷键十二、其它快捷键前言 本博主将用C…...
整懵了,蚂蚁金服4面成功拿下测开offer,涨薪10k,突然觉得跳槽也不是那么难
蚂蚁的面试挺独特的,每轮面试都没有HR约时间,一般是晚上8点左右面试官来一个电话,问是否能面试,能的话开始面,不能就约一个其他时间。 全程4面,前四面技术面,电话面试,最后一面是HR面…...
C++内存分布malloc-free-new-delete的区别和联系
目录 一、内存分布 1.1内存分布图: 1.2 为什么要将bss和data区分开呢? 1.3 堆和栈有什么区别 二、malloc、free;new、delete 2.1 new和delete是如何实现的,new与malloc的异同处 2.2既然有了malloc/free,C为什么还…...
【华为OD机试 2023最新 】 最多颜色的车辆(C++ 100%)
文章目录 题目描述输入描述输出描述用例题目解析C++题目描述 在一个狭小的路口,每秒只能通过一辆车,假设车辆的颜色只有 3 种,找出 N 秒内经过的最多颜色的车辆数量。 三种颜色编号为0 ,1 ,2 输入描述 第一行输入的是通过的车辆颜色信息 [0,1,1,2] 代表4 秒钟通过的车…...
Linux安全加固
一、重要文件 /etc/passwd #记录本地用户的属性信息,如UID、GID /etc/shadow #存放用户的口令信息 只有系统管理员能查看 /etc/pam.d/system-auth #账户安全配置文件 /etc/login.defs #修改登录的配置文件 /etc/profile …...
Java基础学习(6)
Java基础学习一 字符串1.1 API 与 API文档1.1.1 如何使用帮助文档查找想要导用的方法1.2 String 概述1.3 创建String对象的两种方式第一种第二种1.4 Java常用字符串方法1.4.1 比较1.4.2 字符串通过索引取出1.4.3 取出字符串中的单个字符1.4.4 替换出字符串当中的字符1.4.5 取出…...
【LeetCode】链表练习 9 道题
第一题:移除链表元素 题目描述: 给你一个链表的头节点head和一个整数val,请你删除链表中所有满足Node.val val的节点,并返回新的头节点 。 列表中的节点数目在范围 [0, 10^4] 内1 < Node.val < 500 < val < 50 /…...
轴承远程监控系统解决方案
一、项目背景 随着现代机械设备朝着高集成、高精密度、系统化、自动化的方向发展,在工业生产中一旦机器发生故障,即使局部失灵,都可能导致设备工作失效,甚至造成整个自动化车间停产,从而给工业生产带来巨大的损失。轴承…...
阿里云轻量服务器Workbench root远程连接和一键连接的区别
阿里云轻量应用服务器远程连接支持Workbench root用户连接和Workbench一键连接,Workbench root需要输入root密码,一键连接不需要输入密码,但是也无法获得root权限,阿里云百科来详细说下阿里云轻量应用服务器远程连接说明ÿ…...
带你用纯C实现一个内存池(图文结合)
为什么要用内存池 为什么要用内存池?首先,在7 * 24h的服务器中如果不使用内存池,而使用malloc和free,那么就非常容易产生内存碎片,早晚都会申请内存失败;并且在比较复杂的代码或者继承的屎山中,…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
