前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何选择数据结构和算法(转)

如何选择数据结构和算法(转)

作者头像
Michael阿明
发布2021-02-20 11:04:22
4280
发布2021-02-20 11:04:22
举报
文章被收录于专栏:Michael阿明学习之路

熟知每种数据结构和算法的功能、特点、时间空间复杂度,还是不够的。工程上的问题往往都比较开放,往往需要综合各种因素,比如编码难度、维护成本、数据特征、数据规模等,最终选择一个工程的最合适解,而非理论上的最优解。

1. 时间、空间复杂度 != 性能

  • 复杂度不是执行时间和内存消耗的精确值 大O表示法表示复杂度的时候,复杂度给出的只能是一个非精确量值的趋势。
  • 代码的执行时间有时不跟时间复杂度成正比 时间复杂度是O(nlogn)的算法,比时间复杂度是O(n2)的算法,执行效率要高。前提是,算法处理的是大规模数据的情况。
  • 对于处理不同问题的不同算法,其复杂度大小没有可比性

2. 抛开数据规模谈数据结构和算法都是“耍流氓”

  • 在数据规模很小的情况下,普通算法和高级算法之间的性能差距会非常小。 大部分情况下,我们直接用最简单的存储结构和最暴力的算法就可以了。

比如,对于长度在100以内的字符串匹配,我们直接使用朴素的字符串匹配算法就够了。如果用KMP、BM这些更加高效的字符串匹配算法,实际上就大材小用了。因为这对于处理时间是毫秒量级敏感的系统来说,性能的提升并不大。相反,这些高级算法会徒增编码的难度,还容易产生bug

3. 结合数据特征和访问方式来选择数据结构

如何将一个背景复杂、开放的问题,通过细致的观察、调研、假设,理清楚要处理数据的特征与访问方式,这才是解决问题的重点。

比如前面讲过,Trie树这种数据结构是一种非常高效的字符串匹配算法。但是,如果你要处理的数据,并没有太多的前缀重合,并且字符集很大,显然就不适合利用Trie树了。所以,在用Trie树之前,我们需要详细地分析数据的特点,甚至还要写些分析代码、测试代码,明确要处理的数据是否适合使用Trie 树这种数据结构。

再比如,图的表示方式有很多种,邻接矩阵、邻接表、逆邻接表、二元组等等。你面对的场景应该用哪种方式来表示,具体还要看你的数据特征和访问方式。如果每个数据之间联系很少,对应到图中,就是一个稀疏图,就比较适合用邻接表来存储。相反,如果是稠密图,那就比较适合采用邻接矩阵来存储。

4. 区别对待IO密集、内存密集和计算密集

  • 如果要处理的数据存储在磁盘,比如数据库中。那代码的性能瓶颈有可能在磁盘IO,而并非算法本身。这个时候,你需要合理地选择数据存储格式和存取方式,减少磁盘IO的次数

比如最终推荐人的例子。如果某个用户是经过层层推荐才来注册的,获取他的最终推荐人的时候,就需要多次访问数据库,性能显然不高。 我们知道,某个用户的最终推荐人一旦确定,就不会变动。所以,可以离线计算每个用户的最终推荐人,并且保存在表中的某个字段里。当要查看某个用户的最终推荐人的时候,访问一次数据库就可以获取到。

  • 如果数据是存储在内存中,那还需要考虑,代码是内存密集型的还是CPU密集型的。

所谓CPU密集型,简单点理解就是,代码执行效率的瓶颈主要在CPU执行的效率。我们从内存中读取一次数据,到CPU缓存或者寄存器之后,会进行多次频繁的CPU计算(比如加减乘除),CPU计算耗时占大部分。所以,在选择数据结构和算法的时候,要尽量减少逻辑计算的复杂度。比如,用位运算代替加减乘除运算等。

所谓内存密集型,简单点理解就是,代码执行效率的瓶颈内存数据的存取。对于内存密集型的代码,计算操作都比较简单,比如,字符串比较操作,实际上就是内存密集型的。每次从内存中读取数据之后,我们只需要进行一次简单的比较操作。所以,内存数据的读取速度,是字符串比较操作的瓶颈。因此,在选择数据结构和算法的时候,需要考虑是否能减少数据的读取量,数据是否在内存中连续存储,是否能利用CPU缓存预读

5. 善用语言提供的类,避免重复造轮子

大部分常用的数据结构和算法,编程语言都提供了现成的类和函数实现。比如,Java中的HashMap就是散列表的实现,TreeMap就是红黑树的实现等。除非有特殊的要求,一般直接使用编程语言中提供的这些类或函数。

这些编程语言提供的类和函数,经过无数验证过的,不管是正确性、鲁棒性,都要超过你自己造的轮子。

重复造轮子,并没有那么简单。你需要写大量的测试用例,并且考虑各种异常情况,还要团队能看懂、能维护。出力不讨好。

这也是很多高级的数据结构和算法,比如Trie树、跳表等,在工程中,并不经常被应用的原因。但这并不代表,学习数据结构和算法是没用的。深入理解原理,有助于你能更好地应用这些编程语言提供的类和函数。能否深入理解所用工具、类的原理,这也是普通程序员跟技术专家的区别。

6. 千万不要漫无目的地过度优化

一段代码执行只需要0.01秒,你非得用一个非常复杂的算法或者数据结构,将其优化成0.005秒。这种微小优化的意义也并不大。维护成本高。

要学会估算。不仅要对普通情况下的数据规模和性能压力做估算,还需要对异常以及将来一段时间内,可能达到的数据规模和性能压力做估算。这样,我们才能做到未雨绸缪,写出来的代码才能经久可用。

还有,当你真的要优化代码的时候,一定要先做Benchmark 基准测试。这样才能避免你想当然地换了一个更高效的算法,但真实情况下,性能反倒下降了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/08/22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 时间、空间复杂度 != 性能
  • 2. 抛开数据规模谈数据结构和算法都是“耍流氓”
  • 3. 结合数据特征和访问方式来选择数据结构
  • 4. 区别对待IO密集、内存密集和计算密集
  • 5. 善用语言提供的类,避免重复造轮子
  • 6. 千万不要漫无目的地过度优化
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档