首先,该文章来自于极客时间网站,王争的专栏——《数据结构与算法之美》,我这里只是做简单的解释、记录并添加自己的见解,只是作为个人笔记,若侵权,马上删除。最后建议直接去该网站上购买该课程看原作者的讲解,一来是支持作者,二来是作者写的确实不错。

首先,我们来看一道思考题:假设我们有 1000 万个整数数据,每个数据占 8 个字节,如何设计数据结构和算法,快速判断某个整数是否出现在这 1000 万数据中? 我们希望这个功能不要占用太多的内存空间,最多不要超过 100MB,你会怎么做呢?

无处不在的二分思想

先看一个例子,假设现在有10个订单,订单金额分别是8,11,19,23,27,33,45,55,67,98。我们的任务是寻找是否存在金额等于19元的订单。如果存在,则返回订单数据,如果不存在则返回null。

利用二分思想,每次都与区间的中间数据(如果范围的数字有偶数个,中间数有两个,则选择较小的那个)比对大小,缩小查找区间的范围。如下图所示,low 和 high 表示待查找区间的下标,mid 表示待查找区间的中间元素下标。

阅读全文 »

首先,该文章来自于极客时间网站,王争的专栏——《数据结构与算法之美》,我这里只是做简单的解释、记录并添加自己的见解,只是作为个人笔记,若侵权,马上删除。最后建议直接去该网站上购买该课程看原作者的讲解,一来是支持作者,二来是作者写的确实不错。

现在的编程语言基本都会内置排序函数,比如 C 语言中 qsort(),C++ STL 中的 sort()、stable_sort(),还有 Java 语言中的 Collections.sort()。在平时开发中,我们也都是直接使用这些现成的函数。下面我们就探索一下这些排序函数是如何实现的、底层都利用了哪种排序算法?

基于这个问题,我们就看下排序这部分最后一块内容:如何实现一个通用的、高性能的排序函数?

如何选择合适的排序算法?

我们先回顾一下前面讲过的几种排序算法。

阅读全文 »

首先,该文章来自于极客时间网站,王争的专栏——《数据结构与算法之美》,我这里只是做简单的解释、记录并添加自己的见解,只是作为个人笔记,若侵权,马上删除。最后建议直接去该网站上购买该课程看原作者的讲解,一来是支持作者,二来是作者写的确实不错。

今天主要介绍一下三种时间复杂度为O(n)的排序算法:桶排序、计数排序、基数排序。因为这些排序算法的时间复杂度都是线性的,所以称这类算法为线性排序(Linear sort)。之所以能做到线性的时间复杂度,主要原因是,这三个算法是非基于比较的排序算法,都不涉及元素之间的比较操作

这几种排序算法理解起来都不难,时间、空间复杂度分析起来也很简单,但是对要排序的数据要求很苛刻,所以学习重点的是掌握这些排序算法的适用场景。

我们首先思考一个问题:如何根据年龄给 100 万用户排序?上一节学到的归并、快排也可以完成功能,时间复杂度最低为$O(nlogn)$,有没有更快的排序算法啊?

桶排序(Bucket sort)

阅读全文 »

首先,该文章来自于极客时间网站,王争的专栏——《数据结构与算法之美》,我这里只是做简单的解释、记录并添加自己的见解,只是作为个人笔记,若侵权,马上删除。最后建议直接去该网站上购买该课程看原作者的讲解,一来是支持作者,二来是作者写的确实不错。

上一节讲了冒泡排序、插入排序、选择排序,它们的时间复杂度都是$O(n^2)$,比较高,适合小规模数据的排序。对于比较大规模的数据集,可以使用两种时间复杂度为$O(nlogn)$的排序算法,归并排序和快速排序。

这两种排序方法均用到了分治思想。我们也可以借鉴这个思想,解决非排序问题,比如:如何在 O(n) 的时间复杂度内查找一个无序数组中的第 K 大元素?

归并排序的原理

核心思想还是挺简单的。如果要排序一个数组,先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。

阅读全文 »

首先,该文章来自于极客时间网站,王争的专栏——《数据结构与算法之美》,我这里只是做简单的解释、记录并添加自己的见解,只是作为个人笔记,若侵权,马上删除。最后建议直接去该网站上购买该课程看原作者的讲解,一来是支持作者,二来是作者写的确实不错。

排序非常重要,而且排序算法很多,有很多可能你连名字都没听说过,比如猴子排序、睡眠排序、面条排序等。我只讲众多排序算法中的一小撮,也是最经典的、最常用的:冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序。按照时间复杂度把它们分成了三类,分三节课来讲解。

img

因为我笔记比教程少了一节,所以这上面的11、12、13章节分别对应我这里的10、11、12章节。

带着问题去学习,是最有效的学习方法。所以按照惯例,我还是先给你出一个思考题:插入排序和冒泡排序的时间复杂度相同,都是$O(n^2)$,在实际的软件开发里,为什么我们更倾向于使用插入排序算法而不是冒泡排序算法呢?

阅读全文 »

首先,该文章来自于极客时间网站,王争的专栏——《数据结构与算法之美》,我这里只是做简单的解释、记录并添加自己的见解,只是作为个人笔记,若侵权,马上删除。最后建议直接去该网站上购买该课程看原作者的讲解,一来是支持作者,二来是作者写的确实不错。

现在很多 App 都有这推荐注册返佣金功能。这个功能中,用户 A 推荐用户 B 来注册,用户 B 又推荐了用户 C 来注册。那么,用户 C 的“最终推荐人”为用户 A,用户 B 的“最终推荐人”也为用户 A,而用户 A 没有“最终推荐人”。

一般来说,会通过数据库来记录这种推荐关系。在数据库表中,可以记录两行数据,其中 actor_id 表示用户 id,referrer_id 表示推荐人 id。

img

给定一个用户 ID,如何查找这个用户的“最终推荐人”?

阅读全文 »

首先,该文章来自于极客时间网站,王争的专栏——《数据结构与算法之美》,我这里只是做简单的解释、记录并添加自己的见解,只是作为个人笔记,若侵权,马上删除。最后建议直接去该网站上购买该课程看原作者的讲解,一来是支持作者,二来是作者写的确实不错。

CPU 资源是有限的,任务的处理速度与线程个数并不是线性正相关。相反,过多的线程反而会导致 CPU 频繁切换,处理性能下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置的。

当我们向固定大小的线程池中请求一个线程时,如果线程池中没有空闲资源了,这个时候线程池如何处理这个请求?是拒绝请求还是排队请求?各种处理策略又是怎么实现的呢?

实际上,这些问题并不复杂,其底层的数据结构就是我们今天要学的内容,队列(queue)。

如何理解“队列”?

阅读全文 »

首先,该文章来自于极客时间网站,王争的专栏——《数据结构与算法之美》,我这里只是做简单的解释、记录并添加自己的见解,只是作为个人笔记,若侵权,马上删除。最后建议直接去该网站上购买该课程看原作者的讲解,一来是支持作者,二来是作者写的确实不错。

浏览器的前进、后退功能,我想你肯定很熟悉吧?当你依次访问完一串页面 a-b-c 之后,点击浏览器的后退按钮,就可以查看之前浏览过的页面 b 和 a。当你后退到页面 a,点击前进按钮,就可以重新查看页面 b 和 c。但是,如果你后退到页面 b 后,点击了新的页面 d,那就无法再通过前进、后退功能查看页面 c 了。假设你是 Chrome 浏览器的开发工程师,你会如何实现这个功能呢?

这就要用到我们今天要讲的“栈”这种数据结构。

如何理解“栈”?

关于“栈”,可以想象就是一摞叠在一起的盘子。我们平时放盘子的时候,都是从下往上一个一个放;取的时候,我们也是从上往下一个一个地依次取,不能从中间任意抽出。后进者先出,先进者后出,这就是典型的“栈”结构。

阅读全文 »

Docker 分为客户端和服务端两部分, docker 为客户端调用的命令, dockerd 为服务端调用的命令, 本文着重介绍客户端的用法。

特此声明,下面大部分内容来自于这位大佬,除此之外,还添加了一些自己的理解。

概览

主要用法:docker [ docker命令选项 ] [ 子命令 ] [ 子命令选项 ]

docker [ 子命令 ] —help 可查看每个子命令的详细用法。

阅读全文 »