
数据结构与算法之美
文章平均质量分 90
我们学习数据结构和算法,并不是为了死记硬背几个知识点。我们的目的是建立时间复杂度、空间 复杂度意识,写出高质量的代码,能够设计基础架构,提升编程技能,训练逻辑思维,积攒人生经 验,以此获得工作回报,实现你的价值,完善你的人生。
韩淼燃
最近在更新运维专栏。欢迎大家来点赞,关注。
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
16 | 二分查找(下):如何快速定位IP对应的省份地址?
通过 IP 地址来查找 IP 归属地的功能,不知道你有没有用过?没用过也没关系,你现在可以打开百度,在搜索框里随便输一个 IP 地址,就会看到它的归属地。这个功能并不复杂,它是通过维护一个很大的 IP 地址库来实现的。地址库中包括 IP 地址范围和归属地的对应关系。当我们想要查询 202.102.133.13 这个 IP 地址的归属地时,我们就在地址库中搜索,发现这个 IP 地址落在[202.102.133.0, 202.102.133.255]这个地址范围内,那我们就可以将这个 IP 地址范围对应的归属地原创 2022-07-11 11:02:18 · 1318 阅读 · 0 评论 -
15 | 二分查找(上):如何用最省内存的方式实现快速查找功能?
今天我们讲一种针对有序数据集合的查找算法:二分查找(Binary Search)算法,也叫折半查找算法。二分查找的思想非常简单,很多非计算机专业的同学很容易就能理解,但是看似越简单的东西往往越难掌握好,想要灵活应用就更加困难。老规矩,我们还是来看一道思考题。假设我们有 1000 万个整数数据,每个数据占 8 个字节,如何设计数据结构和算法,快速判断某个整数是否出现在这 1000 万数据中? 我们希望这个功能不要占用太多的内存空间,最多不要超过 100MB,你会怎么做呢?带着这个问题,让我们进入今天的内容吧!原创 2022-07-11 10:59:54 · 237 阅读 · 0 评论 -
14 | 排序优化:如何实现一个通用的、高性能的排序函数?
几乎所有的编程语言都会提供排序函数,比如 C 语言中 qsort(),C++ STL 中的 sort()、stable_sort(),还有 Java 语言中的 Collections.sort()。在平时的开发中,我们也都是直接使用这些现成的函数来实现业务逻辑中的排序功能。那你知道这些排序函数是如何实现的吗?底层都利用了哪种排序算法呢?基于这些问题,今天我们就来看排序这部分的最后一块内容:如何实现一个通用的、高性能的排序函数?如果要实现一个通用的、高效率的排序函数,我们应该选择哪种排序算法?我们先回顾一下前原创 2022-07-11 10:58:12 · 147 阅读 · 0 评论 -
13 | 线性排序:如何根据年龄给100万用户数据排序?
上两节中,我带你着重分析了几种常用排序算法的原理、时间复杂度、空间复杂度、稳定性等。今天,我会讲三种时间复杂度是 O(n) 的排序算法:桶排序、计数排序、基数排序。因为这些排序算法的时间复杂度是线性的,所以我们把这类排序算法叫作线性排序(Linear sort)。之所以能做到线性的时间复杂度,主要原因是,这三个算法是非基于比较的排序算法,都不涉及元素之间的比较操作。这几种排序算法理解起来都不难,时间、空间复杂度分析起来也很简单,但是对要排序的数据要求很苛刻,所以我们今天学习重点的是掌握这些排序算法的适用场景原创 2022-07-11 10:54:14 · 531 阅读 · 0 评论 -
12 | 排序(下):如何用快排思想在O(n)内查找第K大元素?
上一节我讲了冒泡排序、插入排序、选择排序这三种排序算法,它们的时间复杂度都是 O(n2),比较高,适合小规模数据的排序。今天,我讲两种时间复杂度为 O(nlogn) 的排序算法,归并排序和快速排序。这两种排序算法适合大规模的数据排序,比上一节讲的那三种排序算法要更常用。归并排序和快速排序都用到了分治思想,非常巧妙。我们可以借鉴这个思想,来解决非排序的问题,比如:如何在 O(n) 的时间复杂度内查找一个无序数组中的第 K 大元素? 这就要用到我们今天要讲的内容。我们先来看归并排序(Merge Sort)。归并原创 2022-07-07 18:04:58 · 300 阅读 · 0 评论 -
11 | 排序(上):为什么插入排序比冒泡排序更受欢迎?
排序对于任何一个程序员来说,可能都不会陌生。你学的第一个算法,可能就是排序。大部分编程语言中,也都提供了排序函数。在平常的项目中,我们也经常会用到排序。排序非常重要,所以我会花多一点时间来详细讲一讲经典的排序算法。排序算法太多了,有很多可能你连名字都没听说过,比如猴子排序、睡眠排序、面条排序等。我只讲众多排序算法中的一小撮,也是最经典的、最常用的:冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序。我按照时间复杂度把它们分成了三类,分三节课来讲解。带着问题去学习,是最有效的学习方法原创 2022-07-07 17:21:12 · 216 阅读 · 0 评论 -
10 | 递归:如何用三行代码找到“最终推荐人”?
推荐注册返佣金的这个功能我想你应该不陌生吧?现在很多 App 都有这个功能。这个功能中,用户A 推荐用户 B 来注册,用户 B 又推荐了用户 C来注册。我们可以说,用户 C的“最终推荐人”为用户A,用户 B 的“最终推荐人”也为用户 A,而用户 A 没有“最终推荐人”。一般来说,我们会通过数据库来记录这种推荐关系。在数据库表中,我们可以记录两行数据,其中actor_id 表示用户 id,referrer_id 表示推荐人 id。 基于这个背景,我的问题是,给定一个用户 ID,如何查找这个用户的“最终推荐人”原创 2022-07-07 17:03:47 · 114 阅读 · 0 评论 -
09 | 队列:队列在线程池等有限资源池中的应用
我们知道,CPU 资源是有限的,任务的处理速度与线程个数并不是线性正相关。相反,过多的线程反而会导致 CPU 频繁切换,处理性能下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置的。当我们向固定大小的线程池中请求一个线程时,如果线程池中没有空闲资源了,这个时候线程池如何处理这个请求?是拒绝请求还是排队请求?各种处理策略又是怎么实现的呢?实际上,这些问题并不复杂,其底层的数据结构就是我们今天要学的内容,队列(queue)。队列这个概念非常好理解。你可以把它想象成排队买票,先来的先买原创 2022-07-07 15:06:36 · 150 阅读 · 1 评论 -
08 | 栈:如何实现浏览器的前进和后退功能?
浏览器的前进、后退功能,我想你肯定很熟悉吧?当你依次访问完一串页面 a-b-c 之后,点击浏览器的后退按钮,就可以查看之前浏览过的页面 b 和a。当你后退到页面 a,点击前进按钮,就可以重新查看页面 b 和 c。但是,如果你后退到页面 b后,点击了新的页面 d,那就无法再通过前进、后退功能查看页面 c 了。假设你是 Chrome 浏览器的开发工程师,你会如何实现这个功能呢?这就要用到我们今天要讲的“栈”这种数据结构。带着这个问题,我们来学习今天的内容。关于“栈”,我有一个非常贴切的例子,就是一摞叠在一起的盘原创 2022-07-07 11:05:59 · 777 阅读 · 0 评论 -
07 | 链表(下):如何轻松写出正确的链表代码?
上一节我讲了链表相关的基础知识。学完之后,我看到有人留言说,基础知识我都掌握了,但是写链表代码还是很费劲。哈哈,的确是这样的。想要写好链表代码并不是容易的事儿,尤其是那些复杂的链表操作,比如链表反转、有序链表合并等,写的时候非常容易出错。从我上百场面试的经验来看,能把“链表反转”这几行代码写对的人不足10%。为什么链表代码这么难写?究竟怎样才能比较轻松地写出正确的链表代码呢?只要愿意投入时间,我觉得大多数人都是可以学会的。比如说,如果你真的能花上一个周末或者一整天的时间,就去写链表反转这一个代码,多写几遍,原创 2022-07-06 17:54:30 · 149 阅读 · 0 评论 -
06 | 链表(上):如何实现LRU缓存淘汰算法?
今天我们来聊聊“链表(Linked list)”这个数据结构。学习链表有什么用呢?为了回答这个问题,我们先来讨论一个经典的链表应用场景,那就是 LRU 缓存淘汰算法。缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如常见的 CPU 缓存、数据库缓存、浏览器缓存等等。缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略 FIFO(First In,First Out)、最少使用策略LFU(Leas原创 2022-07-06 11:29:46 · 169 阅读 · 0 评论 -
05 | 数组:为什么很多编程语言中数组都从0开始编号?
提到数组,我想你肯定不陌生,甚至还会自信地说,它很简单啊。是的,在每一种编程语言中,基本都会有数组这种数据类型。不过,它不仅仅是一种编程语言中的数据类型,还是一种最基础的数据结构。尽管数组看起来非常基础、简单,但是我估计很多人都并没有理解这个基础数据结构的精髓。在大部分编程语言中,数组都是从 0 开始编号的,但你是否下意识地想过,为什么数组要从 0 开始编号,而不是从 1 开始呢? 从 1 开始不是更符合人类的思维习惯吗?你可以带着这个问题来学习接下来的内容。什么是数组?我估计你心中已经有了答案。不过,我还原创 2022-07-05 17:25:10 · 195 阅读 · 0 评论 -
04 | 复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度
上一节,我们讲了复杂度的大 O表示法和几个分析技巧,还举了一些常见复杂度分析的例子,比如 O(1)、O(logn)、O(n)、O(nlogn) 复杂度分析。掌握了这些内容,对于复杂度分析这个知识点,你已经可以到及格线了。但是,我想你肯定不会满足于此。今天我会继续给你讲四个复杂度分析方面的知识点,最好情况时间复杂度(best case time complexity)、最坏情况时间复杂度(worst case time complexity)、平均情况时间复杂度(average case time compl原创 2022-07-04 20:45:44 · 230 阅读 · 0 评论 -
03 | 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?
我们都知道,数据结构和算法本身解决的是“快”和“省”的问题,即如何让代码运行得更快,如何代码更省存储空间。所以,执行效率是算法一个非常重要的考量指标。那如何来衡量你编写的算法代码的执行效率呢?这里就要用到我们今天要讲的内容:时间、空间复杂度分析。 其实,只要讲到数据结构与算法,就一定离不开时间、空间复杂度分析。而且,我个人认为,复杂度分析是整个算法学习的精髓,只要掌握了它,数据结构和算法的内容基本上就掌握了一半。复杂度分析实在太重要了,因此我准备用两节内容来讲。希望你学完这个内容之后,无论在任何场景下,面对原创 2022-07-03 20:56:16 · 113 阅读 · 0 评论 -
02 | 如何抓住重点,系统高效地学习数据结构与算法?
你是否曾跟我一样,因为看不懂数据结构和算法,而一度怀疑是自己太笨?实际上,很多人在第一 次接触这门课时,都会有这种感觉,觉得数据结构和算法很抽象,晦涩难懂,宛如天书。正是这个 原因,让很多初学者对这门课望而却步。 我个人觉得,其实真正的原因是你没有找到好的学习方法,没有抓住学习的重点。实际上,数据结构和算法的东西并不多,常用的、基础的知识点更是屈指可数。只要掌握了正确的学习方法,学起来并没有看上去那么难,更不需要什么高智商、厚底子。还记得大学里每次考前老师都要划重点吗?今天,我就给你划划我们这门课的重点原创 2022-07-03 19:09:20 · 136 阅读 · 0 评论 -
01 | 为什么要学习数据结构和算法?
你是不是觉得数据结构和算法,跟操作系统、计算机网络一样,是脱离实际工作的知识?可能除了 面试,这辈子也用不着?原创 2022-07-03 17:45:20 · 147 阅读 · 0 评论