1984年图灵奖颁给了尼古拉斯·沃斯(Niklaus Wirth)。
他有一句名言:
Algorithm+Data Structures=Programs
程序=算法+数据结构
在这个算法和数据结构被忽视的时代。程序员的日常工作,真的离算法和数据结构很远吗?
我先很肤浅地理解计算机,计算机由软件和硬件组成,接收来自键盘,摄像头,网卡,麦克风等硬件的输入,转换为数据,这些数据经过操作系统和各种程序的计算后,得出结果再输出到硬盘、显示器、网卡等各种硬件。所以毕加索(Pablo Picasso)有句名言:
Computers are useless. They can only give you answers.
计算机毫无用处,除了答案,什么也给不了。
我们是程序员,重点关注的不是硬件设备如何把输入的信号转为数据,也不必过多关注输出的数据如何转换为硬件设备需要的信号。程序员最重要的工作就是编写程序,让程序正确、高效、稳定地处理输入的数据,计算出结果输出。所以从这个角度讲,输入不仅仅是程序中函数的入参,还包括从数据库提取的数据、文件中读取的内容,输入也不仅仅是函数的返回,还包括写入磁盘的内容,存入数据库的数据等等。
程序处理输入数据,转换为输出数据的过程叫做算法,这些数据之间的关系叫做数据结构。编写程序第一步肯定是仔细研究数据之间的关系,设计出数据结构,才能去设计算法。算法严重依赖数据结构。所以第一步是数据结构,第二步才是算法。
再看看程序员们日常头痛的问题:
- bug似乎无穷无尽;
- 代码难以维护;
- 需求不断变更,开发跟不上产品的想法;
- 性能问题简直是折磨;
- 安全问题一不小心就给企业造成巨大损失;
要解决这五个问题,要养成良好的编程习惯及工作流程:
- 无论如何,第一步也是最重要的步骤是认真研究、深刻理解需求;
- 根据需求理清数据之间的关系,设计出数据库表结构以及程序中的数据结构;
- 根据设计好的数据结构设计出程序的处理逻辑也就是算法,画出流程图、时序图;
- 对输入进行数据校验和场景判断,这里所说的输入不仅是用户输入,还包括文件、数据库等;
- 根据设计好的算法编写程序;
- 对程序中各种可能的异常场景,比如网络、文件异常进行处理;
- 仔细对程序进行测试;
- 最后一步,编写规范的文档,和程序一起交付给需求方。
本专栏不讨论如何研究与理解需求,重点在第二步根据需求设计数据结构。程序员都是实用主义者,所有的数据结构都是建立在现实需求的基础上。
提起数据结构,大部分人都会觉得高深、困难,实际不然,第一:学习数据结构不需要太高的数学基础,不需要懂微积分、概率论、线性代数等高等数学知识;其次:学习数据结构不需要昂贵的硬件设备,普通的电脑完全可以胜任;第三:学习数据结构只需要掌握一门编程语言最基础的语法和极少数的API,不需要太深的编程语言基础。
数据结构,因为研究的是数据之间的关系,而不是数据本身。为了更清晰地理解,本专栏在数据结构中,会使用两种图形抽象:
- 数据抽象为一个点,数据之间的关系会抽象为一条线;
- 数据之间的关系抽象为一张表,数据抽象为单元格。
对于常见的数据结构,我整理了以下表格
分类 | 数据结构 | 需求/用途 |
1 数组 | 存储数据、数组列表、二叉堆 | |
线性代数、动态规划 | ||
2 线性结构 | 存储数据、队列、栈 | |
存储数据 | ||
广度搜索 | ||
实现递归、宽度搜索、表达式解析 | ||
3 堆 | 优先队列 | |
可合并优先队列 | ||
可合并优先队列 | ||
4 表 | 有序集合、有序映射 | |
存储数据快速检索、无序集合 | ||
5 位图 | 5.1 位集 | 用于压缩存储数据 |
6 树 | 存储树状关系数据的检索方法 | |
6.2 二叉树遍历 | ||
快速查找 | ||
快速查找、有序映射 | ||
数据压缩 | ||
快速查找 | ||
6.7 并查集 | ||
6.8 B-树 | 磁盘存储,快速查找,m=4的B-树就是2-3-4树 | |
磁盘存储,快速查找 | ||
6.10 Fenwick树 | 快速求前几项和 | |
6.11 2-3-4树 | ||
6.12 段树 |
特别地,对于图,需要学习以下算法
7 图基础 | 7.1 图的介绍 | 点、边、路径、权重等概念 |
7.2 图的几种分类 | 加权图、无权图、有向无环图 | |
7.3 图的几种存储方式 | 邻接矩阵、邻接数组等方式 | |
8 搜索算法 | 三色标记DFS | |
三色标记BFS | ||
9 最短路径 | A*算法 | |
单源BFS | ||
单源Dijkstra | ||
加权Floyd-Warshall | ||
单源Bellman-Ford | ||
单源Johnson | ||
10 最大流问题 | Dinitz算法 | |
Ford-Fulkerson | ||
Augmenting Path Algorithm | ||
11 最小生成树 | prim算法 | |
Kruskal算法 | ||
Reverse-Delete | ||
Borüvka Algorithm | ||
12 强连接组件 | Kosaraju’s Algorithm | |
DFS算法 | ||
13 拓扑排序 | Kahn算法 | |
DFS算法 |