![](https://img-blog.csdnimg.cn/20201014180756927.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
数据结构与算法从入门到实战
文章平均质量分 79
基础知识就像是一座大楼的地基,它决定了我们的技术高度。而要想快速做出点事情,前提条件一定是基础能力过硬,“内功”要到位。对于计算机学科来说,最重要的基础知识莫过于数据结构和算法了。
在本专栏中,对于每种数据结构和算法的讲解,都会由浅入深,结合实际的软件开发场景来理解其应用。
码农谷阿莫
程序员的自我修养
展开
-
知识点二十九:并行计算
前言时间复杂度是衡量算法执行效率的一种标准。但是,时间复杂度并不能直接跟性能划等号。在真实的软件开发中,即便在不降低时间复杂度的情况下,也可以通过一些优化手段,提升代码的执行效率。毕竟,对于实际的软件开发来说,即便是像 10%、20% 这样微小的性能提升,也是非常可观的。算法的目的就是为了提高代码执行的效率。那当算法无法再继续优化的情况下,我们该如何来进一步提高执行效率呢?针对这个问题有一种非...原创 2020-02-07 21:47:53 · 2865 阅读 · 0 评论 -
知识点二十八:索引
前言在实际的软件开发中,业务纷繁复杂,功能千变万化,但是,万变不离其宗,如果抛开这些业务和功能的外壳,其实它们的本质都可以抽象为“对数据的存储和计算”。对应到数据结构和算法中,那“存储”需要的就是数据结构,“计算”需要的就是算法。对于存储的需求,功能上无外乎增删改查。这其实并不复杂。但是,一旦存储的数据很多,那性能就成了这些系统要关注的重点,特别是在一些跟存储相关的基础系统(比如 MySQL ...原创 2020-02-06 17:13:41 · 411 阅读 · 0 评论 -
知识点二十七:位图、布隆过滤器
前言网页爬虫是搜索引擎中的非常重要的系统,负责爬取几十亿、上百亿的网页。爬虫的工作原理是,通过解析已经爬取页面中的网页链接,然后再爬取这些链接对应的网页。但是,同一个网页链接有可能被包含在多个页面中,这就会导致爬虫在爬取的过程中,重复爬取到相同的网页。那么,如何避免这些重复的爬取呢?最容易想到的方法就是,我们记录已经爬取过的网页链接(也就是uniform resource locator;UR...原创 2020-02-05 20:44:20 · 447 阅读 · 0 评论 -
知识点二十六:B树/B+树
前言数据库作为主流的数据存储系统,在日常的业务开发中有着举足轻重的地位。在工作中,为了加速数据库中数据的查找速度,常用的处理思路是对表中的数据创建索引。那数据库的索引到底是如何实现的呢?底层使用的是什么数据结构和算法呢?定义问题的需求思考的过程比结论更重要,解决问题的前提是定义清楚问题,那如何定义清楚问题呢?除了对问题进行详细的调研,还有一个办法,那就是,通过对一些模糊的需求进行假设,来限定...原创 2020-02-04 18:03:35 · 581 阅读 · 0 评论 -
知识点二十五:启发式搜索算法——A*算法
前言魔兽世界、仙剑奇侠传这类 MMRPG(Multiplayer Online Role-PlayingGame) 游戏中,有一个非常重要的功能,那就是人物角色的自动寻路。当人物处于游戏地图中的某个位置的时候,我们用鼠标点击另外一个相对较远的位置,人物就会自动地绕过障碍物走过去。这个功能是怎么实现的呢?路径搜索问题实际上,这是一个非常典型的路径搜索问题。人物的起点就是他当下所在的位置,终点就...原创 2020-02-03 18:05:09 · 18161 阅读 · 4 评论 -
知识点二十四:最短路径——Dijkstra 算法
前言像 Google 地图、百度地图、高德地图这样的地图软件,你只需要输入起始、结束地址,地图就会给你规划一条最优出行路线。这里说的最优路线,有很多种定义,比如最短路线、最少用时路线、最少红绿灯路线等等。那么,这些地图软件的最优路线是如何计算出来的吗?底层依赖了什么算法呢?图的最短路径问题关于图上的搜索算法,深度优先搜索和广度优先搜索这两种算法主要是针对无权图的搜索算法。而针对有权图,也就是...原创 2020-02-02 00:15:51 · 3536 阅读 · 1 评论 -
知识点二十三:拓扑排序
前言我们知道,一个完整的项目往往会包含很多代码源文件。编译器在编译整个项目的时候,需要按照依赖关系,依次编译每个源文件。比如,A.cpp 依赖 B.cpp,那在编译的时候,编译器需要先编译 B.cpp,才能编译 A.cpp。编译器通过分析源文件或者程序员事先写好的编译配置文件(比如 Makefile 文件),来获取这种局部的依赖关系。那编译器又该如何通过源文件两两之间的局部依赖关系,确定一个...原创 2020-01-31 17:18:57 · 837 阅读 · 0 评论 -
知识点二十二:动态规划
前言淘宝的“双十一”购物节有各种促销活动,比如“满 200 元减 50 元”。假设你女朋友的购物车中有 n 个(n>100)想买的商品,她希望从里面选几个,在凑够满减条件的前提下,让选出来的商品价格总和最大程度地接近满减条件(200 元),这样就可以极大限度地“薅羊毛”。作为程序员的你,如何编个程序来帮她搞定这个问题呢?要想高效地解决这个问题,就要用到动态规划(Dynamic Progra...原创 2020-01-31 00:16:00 · 715 阅读 · 0 评论 -
知识点二十一:回溯算法
前言回溯算法思想非常简单,但是应用却非常广泛。它除了用来指导像深度优先搜索这种经典的算法设计之外,还可以用在很多实际的软件开发场景中,比如正则表达式匹配、编译原理中的语法分析等。除此之外,很多经典的数学问题都可以用回溯算法来解决,比如数独、八皇后、0-1 背包、图的着色、旅行商问题、全排列等等。既然应用如此广泛,我们今天就来学习一下这个算法思想,看看它是如何指导我们解决问题的。如何理解“回溯算...原创 2020-01-27 16:49:28 · 414 阅读 · 0 评论 -
知识点二十:分治算法
前言MapReduce 是 Google 大数据处理的三驾马车之一,另外两个是 GFS 和 Bigtable。MapReduce在倒排索引、PageRank 计算、网页分析等搜索引擎相关的技术中都有大量的应用。尽管开发一个 MapReduce 看起来很高深,感觉跟我们遥不可及。实际上,万变不离其宗,它的本质就是分治算法。如何理解分治算法?分治算法(divide and conquer)的核心...原创 2020-01-24 16:20:28 · 1045 阅读 · 0 评论 -
知识点十九:贪心算法
前言贪心算法、分治算法、回溯算法、动态规划这 4 个算法思想,原理解释起来都不难,但是要真正掌握且灵活应用它们,并不是件容易的事情。确切地说,它们应该算是算法思想,并不是具体的算法,常用来指导我们设计具体的算法和编码等。因此,学习这类算法思想最好的方法是,结合具体的问题,感受这些算法是怎么工作的,是如何解决问题的,在问题中体会这些算法的本质。这比单纯记忆其原理和定义要更有价值。如何理解“贪心算...原创 2020-01-24 00:06:04 · 680 阅读 · 0 评论 -
知识点十八:字符串匹配算法(Ⅳ)—— AC自动机
前言很多支持用户发表文本内容的网站,比如 BBS,大都会有敏感词过滤的功能,用来过滤掉用户输入的一些淫秽、反动、谩骂等内容。这个功能是怎么实现的呢?实际上,这个功能最基本的原理就是字符串匹配算法,也就是通过维护一个敏感词字典,当用户输入一段文字内容之后,通过字符串匹配算法,来查找用户输入的这段文字是否包含敏感词。如果有,就用“ *** ”把它替代掉。前面几节我们学习了好几种字符串匹配算法,有...原创 2020-01-21 22:08:04 · 576 阅读 · 0 评论 -
知识点十八:字符串匹配算法(Ⅲ)—— Trie 树
前言搜索引擎的搜索关键词提示功能,我们几乎天天在用。为了方便快速输入,当我们在搜索引擎的搜索框中,输入要搜索的文字的某一部分的时候,搜索引擎就会自动弹出下拉框,里面是各种关键词提示,我们可以直接从下拉框中选择我们要搜索的东西,而不用把所有内容都输入进去,一定程度上节省了我们的搜索时间。像 Google、百度这样的搜索引擎,它们的关键词提示功能非常全面和精准,肯定做了很多优化,但万变不离其宗,...原创 2020-01-20 15:57:27 · 715 阅读 · 0 评论 -
知识点十八:字符串匹配算法(Ⅱ)—— KMP/BM算法
前言文本编辑器中的查找替换功能,大家都不陌生。比如,我们在 Word 中把一个单词统一替换成另一个,用的就是这个功能。用上一节讲的 BF 算法和 RK 算法,可以实现这个功能,但是在某些极端情况下,BF 算法性能会退化的比较严重,而 RK 算法需要用到哈希算法,而设计一个可以应对各种类型字符的哈希算法并不简单。对于工业级的软件开发来说,我们希望算法尽可能的高效,并且在极端情况下,性能也不要退化的...原创 2020-01-13 17:38:18 · 1021 阅读 · 0 评论 -
知识点十八:字符串匹配算法(Ⅰ)—— BF/RK算法
前言字符串匹配这样一个功能,对于任何一个开发工程师来说,应该都不会陌生。我们用的最多的就是编程语言所提供的字符串查找函数,比如 Java 中的 indexOf(),Python 中的 find() 函数等,它们底层就是依赖字符串匹配算法。字符串匹配算法很多,两种比较简单的、比较好理解的是:BF 算法和 RK 算法。比较难理解、但更加高效的有:BM 算法和 KMP 算法。这四种都是单模式串匹配算法...原创 2020-01-11 16:51:07 · 791 阅读 · 0 评论 -
知识点十七:哈希算法
前言哈希算法历史悠久,业界著名的哈希算法也有很多,比如 MD5、SHA 等。在平时的开发工作中,基本上都是拿现成的直接用。哈希算法的应用非常非常多,最常见的有:安全加密、唯一标识、数据校验、散列函数、负载均衡、数据分片、分布式存储。那么,在实际的开发中,我们该如何用哈希算法解决问题?什么是哈希算法?实际上,不管是“散列”还是“哈希”,这都只是中文翻译的差别,英文其实都是“Hash”。所以,我...原创 2020-01-09 21:42:07 · 1482 阅读 · 0 评论 -
知识点十六:深度/广度优先搜索
前言在社交网络中,有一个六度分割理论,具体是说,你与世界上的另一个人间隔的关系不会超过六度,也就是说平均只需要六步就可以联系到任何两个互不相识的人。一个用户的一度连接用户很好理解,就是他的好友,二度连接用户就是他好友的好友,三度连接用户就是他好友的好友的好友。在社交网络中,我们往往通过用户之间的连接关系,来实现推荐“可能认识的人”这么一个功能。今天的开篇问题就是:给你一个用户,如何找出这个用户...原创 2020-01-07 17:39:20 · 1043 阅读 · 0 评论 -
知识点十五:图
前言微博、微信、LinkedIn 这些社交软件,大家肯定都玩过吧。在微博中,两个人可以互相关注;在微信中,两个人可以互加好友。那么,微博、微信等这些社交网络的好友关系是如何存储的呢?这就要用到图这种数据结构。实际上,涉及图的算法有很多,也非常复杂,比如图的搜索、最短路径、最小生成树、二分图等等。我们今天主要聚焦在图存储这一方面。什么是“图”?和树比起来,图是一种更加复杂的非线性表数据结构。我...原创 2020-01-06 17:38:07 · 303 阅读 · 0 评论 -
知识点十四:堆和堆排序(下)
前言上一节我们讲了堆和堆排序的一些理论知识,今天我们一起来学习下堆这种数据结构几个非常重要的应用:优先级队列、求 Top K 和求中位数等。堆的应用一:优先级队列优先级队列,顾名思义,它首先应该是一个队列。队列最大的特性就是先进先出。不过,在优先级队列中,数据的出队顺序不是先进先出,而是按照优先级来,优先级最高的,最先出队。如何实现一个优先级队列呢?方法有很多,但是用堆来实现是最直接、最高...原创 2020-01-05 21:54:06 · 299 阅读 · 0 评论 -
知识点十四:堆和堆排序(上)
前言“堆”(Heap),是一种特殊的树。堆这种数据结构的应用场景非常多,最经典的莫过于堆排序了。堆排序是一种原地的、时间复杂度为 O(nlogn) 的排序算法。我们知道,快速排序在平均情况下的时间复杂度为O(nlogn)。尽管这两种排序算法的时间复杂度都是 O(nlogn),甚至堆排序比快速排序的时间复杂度还要稳定,但是,在实际的软件开发中,快速排序的性能要比堆排序好,这是为什么呢?什么是“堆...原创 2019-12-28 12:39:14 · 328 阅读 · 0 评论 -
知识点十三:跳表
前言前面讲过,二分查找底层依赖的是数组随机访问的特性,所以只能用数组来实现。如果数据存储在链表中,就真的没法用二分查找算法了吗?实际上,我们只需要对链表稍加改造,就可以支持类似“二分”的查找算法。我们把改造之后的数据结构叫作跳表(Skip list)。跳表这种数据结构对有些人来说可能会比较陌生,因为一般的数据结构和算法书籍里都不怎么会讲到它。但它确实是一种各方面性能都比较优秀的动态数据结构,可...原创 2019-12-19 19:40:00 · 720 阅读 · 0 评论 -
知识点十二:递归树
前言我们都知道,递归代码的时间复杂度分析起来很麻烦。在排序那一节讲过,如何利用递推公式,求解归并排序和快速排序的时间复杂度,但是,有些情况下,比如快排的平均时间复杂度的分析,用递推公式的话,会涉及非常复杂的数学推导。那么,除了用递推公式这种比较复杂的分析方法,有没有更简单的方法呢?接下来,我们一起来了解下树这种数据结构的一种特殊应用——递归树。某些情况下,借助递归树可以快速分析递归算法的时间复...原创 2019-12-27 12:38:39 · 3791 阅读 · 0 评论 -
知识点十一:红黑树
前言二叉查找树是最常用的一种二叉树,它支持快速插入、删除、查找操作,各个操作的时间复杂度跟树的高度成正比,理想情况下,时间复杂度是 O(logn)。不过,二叉查找树在频繁的动态更新过程中,可能会出现树的高度远大于 log2n 的情况,从而导致各个操作的效率下降。极端情况下,二叉树会退化为链表,时间复杂度会退化到 O(n)。要解决这个复杂度退化的问题,我们需要构建一种不管怎么删除、插入数据,在任何...原创 2019-12-26 21:53:11 · 1012 阅读 · 1 评论 -
知识点十:二叉树
前言树,作为一种非线性数据结构,比线性表、栈、队列等线性数据结构要复杂得多,内容也比较多。关于树,有几个比较常用的概念需要重点掌握,那就是:根节点、叶子节点、父节点、子节点、兄弟节点,还有节点的高度、深度、层数,以及树的高度。而关于树的重要结构主要包括:二叉树、二叉查找树、平衡二叉查找树、红黑树、递归树等。树(Tree)再完备的定义,都没有图直观。下图中画了几棵“树”。我们来看看,这些“树”...原创 2019-12-25 22:19:11 · 362 阅读 · 0 评论 -
知识点九:散列表(下)
前言散列表和链表这两种数据结构,经常会被放在一起使用。在链表那一节,我们知道了如何用链表来实现 LRU 缓存淘汰算法,但是链表实现的 LRU 缓存淘汰算法的时间复杂度是 O(n),当时也提到了我们可以继续优化这个实现思路,比如引入散列表(Hash table)来记录每个数据的位置,将缓存访问的时间复杂度降到 O(1);在跳表那一节,我们讲到 Redis 的有序集合是使用跳表来实现的,跳表可以看作...原创 2019-12-23 16:35:02 · 293 阅读 · 0 评论 -
知识点九:散列表(中)
前言我们知道,散列表的查询效率并不能笼统地说成是 O(1)。它跟散列函数、装载因子、散列冲突等都有关系。如果散列函数设计得不好,或者装载因子过高,都可能导致散列冲突发生的概率升高,查询效率下降。在极端情况下,有些恶意的攻击者,还有可能通过精心构造的数据,使得所有的数据经过散列函数之后,都散列到同一个槽里。如果我们使用的是基于链表的冲突解决方法,那这个时候,散列表就会退化为链表,查询的时间复杂度就...原创 2019-12-22 22:42:18 · 628 阅读 · 0 评论 -
知识点九:散列表(上)
前言Word 这种文本编辑器平时我们应该都经常用到吧,那不知你有没有留意过它的拼写检查功能呢?一旦我们在 Word 里输入一个错误的英文单词,它就会用标红的方式提示“拼写错误”。Word 的这个单词拼写检查功能,虽然很小但却非常实用。你有没有想过,这个功能是如何实现的呢?其实啊,一点儿都不难。其实涉及到的数据结构就是散列表(Hash Table)。掌握了散列表,你就能像微软 Office 的工...原创 2019-12-20 11:03:24 · 794 阅读 · 0 评论 -
知识点八:二分查找(下)
前言通过 IP 地址来查找 IP 归属地的功能,不知道大家有没有用过?没用过也没关系,现在可以打开百度,在搜索框里随便输一个 IP 地址,就会看到它的归属地。这个功能并不复杂,它是通过维护一个很大的 IP 地址库来实现的。地址库中包括 IP 地址范围和归属地的对应关系。当我们想要查询 202.122.110.13 这个 IP 地址的归属地时,我们就在地址库中搜索,发现这个 IP 地址落在 ...原创 2019-12-18 11:23:39 · 375 阅读 · 0 评论 -
知识点八:二分查找(上)
前言二分查找(Binary Search)算法,也叫折半查找算法,是一种非常简单的查找算法,很多非计算机专业的同学很容易就能理解,但是看似越简单的东西往往越难掌握好,想要灵活应用就更加困难。老规矩,我们还是来看一道思考题:假设我们有 1000 万个整数数据,每个数据占 8 个字节,如何设计数据结构和算法,快速判断某个整数是否出现在这 1000 万数据中? 我们希望这个功能不要占用太多的内存空间...原创 2019-12-17 20:55:02 · 511 阅读 · 0 评论 -
知识点七:经典排序算法(下)
前言前两节着重分析了几种常用排序算法的原理、时间复杂度、空间复杂度、稳定性等。今天,我们一起来分析三种时间复杂度为 O(n) 的排序算法:桶排序、计数排序、基数排序。因为这些排序算法的时间复杂度是线性的,所以我们把这类排序算法叫作线性排序(Linear sort)。之所以能做到线性的时间复杂度,主要原因是,这三个算法是非基于比较的排序算法,都不涉及元素之间的比较操作。这几种排序算法理解起来都不...原创 2019-12-16 12:01:14 · 225 阅读 · 0 评论 -
知识点七:经典排序算法(中)
前言前面一节讲了冒泡排序、插入排序、选择排序这三种排序算法,它们的时间复杂度都是O(n2),复杂度比较高,适合小规模数据的排序。今天,本文主要讲另外两种时间复杂度为 O(nlogn) 的排序算法,归并排序和快速排序。这两种排序算法适合大规模的数据排序,比前面那三种排序算法要更常用。归并排序和快速排序都用到了分治思想,非常巧妙。我们可以借鉴这个思想,来解决非排序的问题,比如:如何在 O(n) 的...原创 2019-12-10 22:16:59 · 380 阅读 · 0 评论 -
知识点七:经典排序算法(上)
前言排序对于任何一个程序员来说,可能都不会陌生。你学的第一个算法,可能就是排序。大部分编程语言中,也都提供了排序函数。在平常的项目中,我们也经常会用到排序。排序非常重要,但排序算法太多了,有很多可能你连名字都没听说过,比如猴子排序、睡眠排序、面条排序等。本文只挑选出众多排序算法中的一小撮,也是最经典的、最常用的来讲:冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序。...原创 2019-12-08 21:40:14 · 304 阅读 · 0 评论 -
知识点六:递归
前言推荐注册返佣金的这个功能,我想大家应该不陌生吧?现在很多 App 都有这个功能。这个功能中,用户 A 推荐用户 B 来注册,用户 B 又推荐了用户 C 来注册。我们可以说,用户 C 的“最终推荐人”为用户 A,用户 B 的“最终推荐人”也为用户 A,而用户 A 没有“最终推荐人”。一般来说,我们会通过数据库来记录这种推荐关系。在数据库表中,我们可以记录两行数据,其中 actor_id 表示...原创 2019-12-07 16:38:31 · 398 阅读 · 0 评论 -
知识点五:队列
前言我们知道,CPU 资源是有限的,任务的处理速度与线程个数并不是线性正相关。相反,过多的线程反而会导致 CPU 频繁切换,处理性能下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置的。当我们向固定大小的线程池中请求一个线程时,如果线程池中没有空闲资源了,这个时候线程池如何处理这个请求?是拒绝请求还是排队请求?各种处理策略又是怎么实现的呢?实际上,这些问题并不复杂...原创 2019-12-05 13:26:26 · 254 阅读 · 0 评论 -
知识点四:栈
前言浏览器的前进、后退功能,想必大家肯定很熟悉吧?当你依次访问完一串页面 a-b-c 之后,在c页面点击浏览器的后退按钮,就可以查看之前浏览过的页面 b 和 a。当你后退到页面 a,点击前进按钮,就可以重新查看页面 b 和 c。但是,如果你后退到页面 b 后,点击了新的页面 d,那就无法再通过前进、后退功能查看页面 c 了。实际上,这就要用到今天要讲的“栈”这种数据结构。如何理解“栈”?...原创 2019-12-04 21:59:28 · 274 阅读 · 0 评论 -
知识点三:链表
前言学习“链表(Linked list)”这个数据结构有什么用呢?为了回答这个问题,我们今天来讨论一个经典的链表应用场景,那就是 LRU 缓存淘汰算法。缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如常见的 CPU 缓存、数据库缓存、浏览器缓存等等。缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的...原创 2019-11-28 17:58:14 · 880 阅读 · 0 评论 -
知识点二:数组
前言提到数组,我想你肯定不陌生,甚至还会自信地说,它很简单啊。是的,在每一种编程语言中,基本都会有数组这种数据类型。不过,它不仅仅是一种编程语言中的数据类型,还是一种最基础的数据结构。尽管数组看起来非常基础、简单,但是很多人都并没有理解这个基础数据结构的精髓。在大部分编程语言中,数组都是从 0 开始编号的,但你是否下意识地想过,为什么数组要从 0 开始编号,而不是从 1 开始呢? 从 1 开...原创 2019-11-27 15:53:24 · 265 阅读 · 0 评论 -
知识点一:算法复杂度分析
前言我们都知道,数据结构和算法本身解决的是“快”和“省”的问题,即如何让代码运行得更快,如何让代码更省存储空间。所以,执行效率是算法一个非常重要的考量指标。那如何来衡量你编写的算法代码的执行效率呢?这里就要用到我们今天要讲的内容:时间、空间复杂度分析。其实,只要讲到数据结构与算法,就一定离不开时间、空间复杂度分析。而且,复杂度分析是整个算法学习的精髓,只要掌握了它,数据结构和算法的内容基本上就...原创 2019-11-25 22:26:19 · 1714 阅读 · 0 评论