C/C++代码性能优化——数据结构和算法

1. 数据结构

合适的数据结构,对代码的性能提升非常明显。针对数据结构,我们不需要可以做到白板手写的程度。只要熟知其特点,然后推导出其应用场景,等到了真正需要时,再查找示例代码来修改应用即可。

1.1. 数组

  • 固定类型固定大小的集合,指针和数组基本等价。
  • 适用于通过索引快速随机访问的场景,有序数据可以通过二分法快速查找。

1.2. 链表

  • 线性数据结构,可以从头往后遍历。
  • 适用于需要频繁插入删除操作的场景,对查找需求较低。普通链表适用于数据量较小的场景。

1.3. 块状链表

  • 块状链表是将数组和链表结合起来的一种数据结构,链表的节点存储一个数组。块状链表有点类似简化的二层B+树,其插入和删除不用像平衡二叉树为了维持平衡进行更多修改,所以其平均性能优于平衡二叉搜索树(包括红黑树、B/B+树)。虽然块状链表节点处数据可以利用二分法,但是受限于链表的特性要遍历进行查找的特性,其综合性能依然低于基本全用二分法查找的平衡二叉搜索树(包括红黑树、B/B+树)。块状链表通常比平衡二叉树更节省空间,因为它们不需要存储平衡信息。
  • 块状链表适用于数据量较小,且内存有限制的,插入和删除需求大于查找需求的场景。

1.4. 栈

  • 后进先出(LIFO)的数据结构。
  • 有后进先出需求的场景。

1.5. 队列

  • 先进先出 (FIFO) 数据结构。
  • 有先进先出需求的场景。

1.6. 树

一个分层的数据结构,其每个节点可以有多个子节点。树有比数组更好的插入删除性能,有比链表更好的随机访问性能。针对树的特性,又进一步进行了优化,不同的树有不同的应用重点。

1.6.1. 二叉搜索树

  • 二叉树结构,其中每个节点的左子树中的值都小于节点的值,右子树中的值都大于节点的值。
  • 因为其特点是数据排列有序,可以快速查找、删除,但是当数据有序性较强时,容易退化为一条线,导致性能降低。可以通过深度优先搜索和广度优先搜索来查找,添加和删除。

1.6.2. 平衡二叉搜索树

为了解决二叉搜索树退化导致性能降低的极端情况,出现了平衡二叉搜索树,其避免出现某一个分支过长的情况,操作性能较二权搜索树更加均衡。但是在数据量巨大时,其树高比较高,导致对根节点的操作性能较差。

AVL树和红黑树都是平衡二权搜索树,红黑树相对于AVL树规则更简单,所以平衡二叉树多使用红黑树。C++中的std::map即采用红黑树实现。

1.6.3. B树

为了优化平衡二叉树在大量数据时容易根节点过深的问题,将平衡二叉树的每个节点存放更多数据。在数据量较小的时候,红黑树的平衡性能更优。在数据量比较大的时候,B树的平衡性能比红黑树更优。并且B树引入了更复杂的节点数据,每个非根节点存放3类数据,Key-Data-Pointer。这样就可以应对更复杂的数据存储查询删除需求。

1.6.4. B+树

B+树优化了B树,将Key和数据统一放到根节点存储,子节点只用来存放Key和指针。相较于B树,B+在操作性能上更稳定起伏小。另外,B+树更容易顺序指定范围从前后往后扫描所有数据。B+树的这种特性非常适用于管理文件系统,现代文件系统NTFS和Ext4均使用B+树来实现。大型的关系型数据MySQL、SQL Serve、Oracle、PostgreSQL均使用B+树来存储管理其主要数据。

1.7. 跳表

跳表是对传统的有序链表进行优化,添加多层索引来加速搜索操作,简单来说它是一个空间换时间的策略。针对数据量的大小,以及对性能的要求,可以相应地设计索引层数。

跳表相比红黑树,算法更简单,在索引层级足够的前提下,可以实现更好的搜索性能。在索引层级较多时,其空间相比红黑树会占用更大,具体取决于跳表层级的设计。

跳表需要预先分配空间,红黑树可以更好的动态扩大缩小。

算法复杂度上,跳表比较红黑树要简单,更容易理解。

红黑树的数据是有序的,缓存一致上能够做到比较好。跳表如果数据量比较大,其过多的层级索引和数据之间可能导致缓存一致性降低。

综合来讲,在40MB(对应1000万int类型数据)数据量以下,跳表的性能要略优于红黑树。如果数据量更多,跳表的性能因为其层级索引缓存一致性问题,性能逐渐不如缓存一致性更好的红黑树。所以相对于复杂的大型文件系统或是大型数据库,更适合使用B+树,数据量少一些的数据管理,更适合红黑树或跳表。内存数据库Redis、键值存储数据库LevelDB都是用跳表来实现主要数据的管理。

块状链表更数组属性,局部随机性表现会比跳表好,但是整体随机性比跳表差。综合来讲,在数据量1K以下,块状链表和跳表性能相当,在更大数据量,跳表性能更优。

1.8. 索引

索引是加快查找的重要方法,针对数据量,可以使用多级索引。根据数据类型,可以使用不同类型的索引。一个大型的数据管理系统一般会根据需求,针对性地创建不同的索引来加快查找、增删的性能。

1.8.1. 聚集索引和非聚集索引

  • 聚集索引是以表中的可以作为唯一标记的列的内容进行排序,这样就可以快速通过聚集索引来进行查找,代价空间消耗大,聚集索引创建消耗时间大。
  • 非聚集索引根据表中某一列的内容和行内容指令建立一个新的表,空间代价小,但是查找性能较聚集索引慢。非聚集索引可以根据不同列的信息来建立多个新的表,针对性的索引查找。

1.8.2. 位图索引

位图索引(Bitmap Index),即用比特来映射数据的状态。为了更好的数据管理实时性能,一般数据删除时,只是在另外的数据结构中标记其是否删除,并不执行真正的删除操作。等到下次需要添加数据时,只需要通过标记找到无效数据节点覆盖新数据即可。这样可以提高性能。更高效,更节省空间的标记数据结构就是位图索引。大型文件系统、数据库都有使用位图索引。

1.9. 哈希表

哈希表是一个存储Key-Value的数据结构。哈希表通过哈希函数将Key映射到存储数据的下标索引,这样就可以快速根据Key来访问对应的数据。哈希函数生成的哈希值下标有可能重复,那么可以用一个链表来记录哈希值相同的Key和数据以便二次查找,所以其空间消耗比红黑树高。但是其搜索性能远高于红黑树。哈希表的算法实现也比较红黑树简单。哈希表有大量的开源算法,可以根据数据类型、数据量、算法复杂度和性能综合选择哈希算法。

1.10. 图

图是由节点和连接节点的边组成的非线性数据结构,用于表示多个对象之间的关系。如果有多对多的复杂关系,且有最短路径这类需求的,可以考虑使用图数据结构。

1.11. 总结

一个大型的存储系统,一般会使用多种数据结构协调工作,甚至根据实际情况会在标准数据结构上进行变形优化。重点是要明白数据结构的特点和和其对应性能的表现,这样指导我们使用和优化数据结构。

参考资料:

Stack Data Structure and Implementation in Python, Java and C/C++

B 树 - OI Wiki

2. 常用算法

2.1. 排序算法

2.1.1. 实测结果

2.1.2. 总结

2.1.3. 结论

  • 不推荐使用冒泡排序,插入排序极其简单,各方面性能均大幅优于冒泡排序。
  • 其他还有一些选择排序、堆排序,以及基于归并排序、桶排序的一些改进算法,其实际测试的效果并没有快速排序和希尔排序效果好,所以此处不统计。
  • 一般情况下,可以根据数据类型、数据量级选择多种排序混合使用。
  • 桶排序适合于数据分布平均,且集中在一个不大的范围内。桶排序是以空间换时间。如果有4096范围内的数据需要排序,可以考虑用桶排序。并且针对桶排序有一些优化算法,如基数排序,计数排序。
  • 希尔排序、归并排序、快速排序,都是同一级别,绝大多数情况下是快速排序效率更高。但是三者之中,只有归并排序是稳定排序算法。但是快速排序虽然效率好,但是空间复杂高较高,而希尔排序不需要额外的空间。
  1. 追求效率,用快速排序。
  2. 追求效率且要求稳定,用归并排序。
  3. 追求效度且尽量小占用空间,用希尔排序。
  4. 数据范围分布小,追求效率用桶排序。桶内排序,根据1,2,3选择相应排序算法。
  5. 数据量小于等于64时,使用插入排序,实际过程中快速排序的空间申请会影响整体的效率。
  6. 针对大部分数据都是有序的数据集,使用插入排序,快速排序此时性能最差。

2.2. 查找算法

2.2.1. 顺序查找

顺序查找,适用于数据量较小的无序数据集。

2.2.2. 二分查找

二分查找,适用于有序的数据集。

2.2.3. 插值查找

针对有均匀分布的有序数据集,可以优化二分查找法,可以通过公式估算采样位置进行查找。在数据量较小时,公式估算的性能开销会抵消插值查找优化的效果。

2.2.4. 斐波那契查找

针对有均匀分布的有序数据集,采用斐波那契数(黄金分割点分割,而不像二分法平分)分割数据进行查找,相比二分法能够更快地找到目标。针对不均匀的数据集,其效果也会远远好于插值查找。数据量较小时,算法额外开销会抵消其部分性能优势。

2.2.5. 二叉查找树查找

如果数据是以平衡二叉树存储的,那么就使用二叉查找树查找法来查找。

2.2.6. 分块查找

数据有序存储的开销较大,无序存储的查找开销大。分块查找,就是部分有序,将数据按大小分成几堆,每一堆中的数据不要求有序。这样初始的存储开销较有序存储低,查找性能较无序存储的高。分块查找要求能够比较准确地将数据分为大小相近的几堆。如果分类不够准确,加大了存储开销,却不能提升多少查找性能。

2.2.7. 哈希查找

哈希查找通过存储数据时建立哈希映射表,查找时通过哈希值来查找。因为哈希表存在冲突,所以哈希表需要存储额外的信息,导致空间消耗较大。所以哈希查找,适用于空间足够,且数据量较大,对查找性能追求的场景。

2.2.8. 表查找

表查找,利用位图索引来查找,速度非常快,但是空间消耗大,且要求数据唯一。针对需要重复快速查询的场景非常有效。如果手机号码是否使用等。如果有相关对应数据需求,可以建立索引表存储内容地址。

2.2.9 布隆过滤器查找

布隆过滤器根据要查询的数据建立一个相应的位图,存储数据时,将数据经过8个哈希算法,生成8个哈希索引,并在位图8个对应的索引上置1。存储其他数据时,也依次如此操作。我们知道哈希索引有冲突的概率,但是8个哈希索引都同时冲突的概率其实不大,但是依然有完全冲突的存在性。所以布隆过滤查找的结果为不存在时,就一定不存在。存在时,就有概率是误差。通过调整哈希算法的个数,可以减少冲突的概率。如邮件服务系统的垃圾邮件一般会使用布隆过滤器查找是否为垃圾邮件。

2.2.10. 结论

  • 无序,且数据集较小(具体数量与CPU性能有关),且不需要频繁查找时,用顺序查找。
  • 无序,且数据集较大,可以先排序,再根据数据特性选择其他有序查找算法。
  • 有序,且数据集较小,使用二分法查找。
  • 有序,且数据集较大,且数据分布均匀,使用插值查找。
  • 有序,且数据集较大,且数据分布不一定均匀,使用斐波那契查找。
  • 数据集为平衡二叉树结构,使用二叉查找树算法查找。
  • 数据分块有序均匀存储,使用分块查找。
  • 目标数据唯一,且有高频查询需求,使用表查找。
  • 快速高频查询需求,且不追求绝对准确的查询是否存在的场景,使用布隆过滤器查找。
  • 快速高频查询需求,且能容忍一定的内存消耗,使用哈希查找,如果只是查询是否存在,可以结合位图索引。

2.3. 其他算法

算法是一种思想,不必熟练到手写算法的程序。知道什么场景用什么算法,具体算法用的时候去查询即可。

  • 分治算法,把一个复杂的问题分成两个或更多的相同或相似的子问题,利用递归来实现。其应用如二分查找,快速排序等。
  • 动态规划,将一个复杂的问题分成多个类似的子问题,且使用额外变量记录每一子问题的结果,后续避免重复计算子问题结果。其应用如背包问题(装入的端口总价值最高)。
  • 贪心算法,很贪心每次都要最优,贪心算法将在问题的每一步选择中都采取当前状态下最优的选择,以期望通过局部最优解最终达到全局最优解。
  • 最短路径算法,其应用如地图两个位置的最短路线规划。
  • 近似算法,有时追求最优解会耗费大量计算资源,而且追求近似解,可以节省大量计算资源用来在其他方面提升性能,最终达到综合性能的提升。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nginx离线安装(保姆级教程)

1、下载与安装gcc-c环境 获取rpm包的方式很多,在这里推荐使用yum工具获取,因为手动从官网下载,手动执行rpm -Uvh *.rpm --nodeps --force命令进行安装,可能会缺少某个依赖,我们也不确定到底需要哪些依赖。 因此需要准…

Java毕业设计 基于springboot医院挂号系统 医院管理系统

Java毕业设计 基于springboot医院挂号系统 医院管理系统 springboot医院挂号系统 医院管理系统 功能介绍 用户:登录 首页 个人资料 修改密码 门诊管理 用户挂号 医生:登录 首页 个人资料 修改密码 门诊管理: 用户挂号 处方划价 项目划价 项目缴费 项目…

【机器学习300问】43、回归模型预测效果明明很好,为什么均方根误差很大?

一、案例描述 假设我们正在构建一个房地产价格预测模型,目标是预测某个城市各类住宅的售价。模型基于大量房屋的各种特征(如面积、地段、房龄、楼层等)进行训练。 回归模型在大部分情况下对于住宅价格预测非常精准,用户反…

【教程】深入探究 JS代码混淆与加密技术

🔒 引言 在网络世界中,保护代码安全是至关重要的一环。JS代码混淆与加密技术则成为了开发者们常用的手段之一。本文将深入探讨混淆和加密的概念,以及其实现原理和应用方法,帮助读者更好地了解并运用这些技术。 ✨ 概念介绍 &quo…

调用百度通用翻译API进行中文翻译(附python代码)

文章目录 1. 百度API2. API接口3. 大规模使用4. AcknowledgmentReference彩蛋:百度大脑 AI开放平台 1. 百度API 在百度翻译开放平台(http://api.fanyi.baidu.com/api/trans/product/desktop)注册账号,可以免费使用基本版翻译功能…

C语言复杂度(个人笔记)

时间复杂度主要衡量一个算法的运行快慢. 空间复杂度主要衡量一个算法运行所需要的额外空间. 时间复杂度 算法中的基本操作的执行次数,为算法的时间复杂度. 只需要大概执行次数,我们使用大O的渐进表示法。(看谁对数学表达式的影响最大) 空间复杂度 是…

论文笔记:Contrastive Multi-Modal Knowledge GraphRepresentation Learning

论文来源:IEEE Transactions on Knowledge and Data Engineering 2023 论文地址:Contrastive Multi-Modal Knowledge Graph Representation Learning | IEEE Journals & Magazine | IEEE Xplorehttps://ieeexplore.ieee.org/abstract/document/9942…

可变形卷积颠覆式创新!新SOTA提速80%,更高性能,更强几何适应能力

在传统的卷积神经网络中,固定模式的卷积核在处理图像时可能会限制网络对不规则形状特征的提取能力。为了解决这个问题,研究者提出了可变形卷积。 可变形卷积是一种改进的卷积操作,它通过引入可学习的偏移量来增强模型对几何变化的适应能力&a…

qt+ffmpeg 实现音视频播放(三)之视频播放

一、视频播放流程 (PS:视频的播放流程跟音频的及其相似!!) 1、打开视频文件 通过 avformat_open_input() 打开媒体文件并分配和初始化 AVFormatContext 结构体。 函数原型如下: int avformat_open_inpu…

python 教你如何创建一个自定义库 colorlib.py

目录 Colorlib 生成代码 模块代码 导入测试 测试一 测试二 应用测试 颜色列表 colorList 随机颜色元组 randcolorTuples 随机颜色字串 randcolorStrings Color类测试 测试一 测试二 题外话 Colorlib 有没有碰到过这样的场景:写代码时想要用上丰富的色…

C#混淆心得

C#混淆心得 近期遇到混淆C#代码的需求,在网上找了很多办法,在此记录一下。 混淆的本质就是让代码变丑,让别人看不懂。 为什么要混淆: 1.保护核心代码 可以在一定程度上避免别人偷代码,从而保护重要的部分&#xf…

3.3 RK3399项目开发实录-板载Ubuntu系统的使用(wulianjishu666)

嵌入式物联网常用90款传感器开发例程。链接:https://pan.baidu.com/s/1oisHMZXDzKqa4EspY83V-A?pwdo5f4 1. 介绍 Ubuntu 使用手册是针对 Firefly 官方发布的 Ubuntu 系统固件特性所编写,适用于 Ubuntu Desktop 与 Minimal 系统,部分与 UI 显…

适用于智能语音小家电的语音ic类型有哪些?

适用于智能语音小家电的语音ic类型有哪些? 1. 语音播放芯片:这种芯片主要用于实现语音提示和报警功能。例如,当按下某个按钮时,它可以发出语音提醒,或者在出现故障时发出报警声音。这种芯片的应用非常广泛&#xff0…

Halcon 条码读取

一维码读取 create_bar_code_model 创建条码读取器的模板 set_bar_code_param 配置解码方式 find_bar_code 读取条码 clear_bar_code_model 清除条码匹配模板 * 1.创建条码读取器的模板 * 参数一:通用参数的名称,针对条形码模型进行调整。默认值为空 * 参…

Java实用经验总结

前言:以下为笔者在工作中总结的好用且简洁代码的经验 文章目录 1、多判断代替if2、通配符替换内容(常见于邮件、短信等模版)3、spring获取bean对象4、动态获取nacos配置5、优雅校验请求入参 1、多判断代替if 针对多个是和否的问题&#xff0…

如何安装和卸载SFP光模块

SFP光模块的安装和拆卸是简单直接的过程。然而,任何非标准操作都可能导致隐式损坏甚至永久故障。您需要参考及时更新的光模块的数据表或用户手册,以熟悉其特性和锁定机制。 准备工作 常见事项 拆卸和插入SFP光模块可能会缩短其使用寿命,因…

搜索二维矩阵

题目链接 搜索二维矩阵 题目描述 注意点 每行中的整数从左到右按非严格递增顺序排列每行的第一个整数大于前一行的最后一个整数1 < matrix.length, matrix[0].length < 100 解答思路 先二分查找找到target所处的行&#xff0c;找到行后再二分查找找到target所处的列…

Java毕业设计-基于springboot开发的数码论坛系统设计与实现-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示&#xff08;源代码在文末&#xff09;二、毕设摘要展示1、开发说明2、需求分析3、系统功能结构 三、系统实现展示1、系统功能模块2、管理员功能模块3、用户后台管理模块 四、毕设内容和源代码获取总结 **Java毕业设计-基于springboot开发的数码论…

计算平均分 javascript

养成好习惯&#xff1a;先写注释再写代码 基础版&#xff1a;直接写逻辑&#xff08;平均分总和/个数&#xff09; // 求平均分 var scores [60, 55, 80, 33, 75, 100]; // 求和,相除 var sum 0; var avg;for (var i 0; i < 6; i) {sum scores[i]; }avg sum / 6; con…

Android Studio 编译报错 ( Could not find com.android.tools.build:gradle:4.2.1.)

检查下根目录下的 build.gradle 配置 , 是否只配置了 jcenter 仓库 &#xff0c;加上 google()mavenCentral() 重新编译试一下