# 前端算法系统练习指南
[在线学习地址](http://47.98.159.95/leetcode-js/)
## 适用人群
如果你正在为**面试**做准备,却对于庞杂的数据结构和算法知识,不知道何从下手;
如果你以前曾经学过一些基础的数据结构或者算法的**基础知识**,却根本没有理解清楚,更不能独立完成算法的设计, 想重新巩固这方面的知识;
如果已经有了一小段工作经验,但却老是在整天`用轮子`、`调API`当中度过,想看一看底层的源码却常常因**编程的内力**不足而放弃;
如果你听说过`LeetCode`这个网站,想要一刷到底,迈向算法巅峰,却因为`浩瀚的题量`和`缺乏系统训练`感到无力,三天打鱼两天晒网,进而感到焦虑,甚至放弃......
如果上面任何一条符合你的现状,那么恭喜你,你来对了地方。作为一个圈内小有名气的[前端博主](https://juejin.im/user/5c45ddf06fb9a04a006f5491),我想借着我的影响力,分享出我系统梳理和练习的过程,希望能够帮助到更多跟我一样遇到类似困难的人,让你少一些不必要的折腾。
全程使用的语言是 `JavaScript`,因此标题上说的是前端xxx, 但实际上你也知道,数据结构和算法这东西主要是考验一个人的思维,至于语言,其中并没有用到任何 JS 的高深语法特性,只要有所编程经验,能理解代码是完全没有问题的,这一点大家放心。
## 算法没有用?
我想各位当中肯定有准备面试的同学,那么你肯定听说过`面试造火箭,工作拧螺丝`, 不少人也拿这句话拿来诟病当前一些互联网大厂的算法面试,因此就有这样的言论: **除了应付面试,学算法其实没啥用**。
这句话我并不完全反对,因为现在随着技术生态的发展,各位走在领域前沿的大牛们已经给大家备足了轮子,遇到一般的业务问题直接把人家的方案拿到用就可以了,另外我也看到过一句话,刚开始觉得扯淡,后来想想觉得有那么一丝道理:
> 凡是需要跨过一定智商门槛才能掌握的技术,都不会轻易的流行。
换句话说:技术变得更简单,别人更愿意用,更容易流行。
这也是当前各种技术框架的真实写照: **足够好用,足够简单,简单到你不需要知道底层复杂的细节**。
那么问题来了,作为一个集智慧和才华于一身的程序员,自己的价值在哪里?
我觉得价值的大小取决于你能够解决的问题,如果说照着设计稿画出一个简单的 Button,你能完成,别的前端也能完成,甚至后后端的同学都能把效果差不多做出来,那这个时候就谈何个人价值?只不过在一个随时**可替代**的岗位上完成了大多数人能轻易做到的事情,张三来完成,或者李四来完成,其实没什么区别。
但是现在如果面对的是一个复杂的工程问题,需要你来开发一个辅助业务的**脚手架工具**,改造**框架源码**来提高项目的扩展性,或者面对严重的性能问题能马上分析出原因,然后给出解决的思路并在不同因素中**平衡**,这些都不是一个业余的玩家能够在短时间内胜任的,这就是体现自己价值的地方。
回到算法本身,它代表的是你**解决更加复杂问题**能力的一部分。
可能干讲不容易理解,我们以 Vue 这个框架为例,如果你以前没有接触过`深度优先遍历`和`递归`的概念,没有看过相应的代码,那么虚拟 DOM 整个`patch`的源码你是基本不可能看懂的;如果你没有系统掌握过`栈`先进后出这种特点的应用,你也是很难理解 Vue 模板编译阶段为什么要用栈来检查标签是否正常闭合;同样的,如果你没有`回溯`这种算法的代码经验,你也是很难理解 Vue 模板编译的优化阶段,到底是怎样在**从父到子**深度优先遍历的过程中**检查到非静态的子节点**后给父节点打上标记;并且,如果你以前不知道 `LRU 缓存淘汰算法`究竟是个什么东西,你看到`keep-alive`组件的实现这里会非常纳闷:
```js
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
}
```
缓存命中了,为什么还要维护一个 keys 数组,而且把这个 key 从数组中删了,又要放到末尾,啥操作呢?
如果之前有相应算法基础,你反而会觉得这个理所当然的事情。
看到了吧?你觉得尤大能写出优秀的“明星”项目,会没有一点扎实的数据结构和算法功底?
当然不仅仅是前端领域,我想服务端也是差不多的情况,这里就不多举例了。
所以各位,我认为基本的算法能力对于一个想要解决复杂问题的工程师而言,不是一个加分项,而且是**必备项**。算法有大用。
## 如何系统练习?
接下来我来分享一下练习数据结构和算法的一些心得,或者方法,分两个关键字来说: **系统**和**练习**。
### 专题突破
如何做到**系统**的训练?
我想按照`LeetCode`上的顺序一题一题刷肯定不是系统,大部分情况是相邻的几个题毫无联系,这一题做了个链表相关的,下一题又是一个哈希表,再下一题又了一个二分搜索树,思维不断的跳转,一方面可能你的基础很薄弱,各个数据结构和算法理解的并不深刻,反复跳到你不熟悉的地方,会挫败你的信心,增加焦虑,甚至直接劝退,另一方面,可能再遇到难一点的`链表`题,你又不会了,可能你会纳闷: 不是刚刚才写出来了一个链表题吗?怎么现在又不会了?容易让你产生自我怀疑,也会影响你训练的自驱力。
因此我觉得分专题各个训练突破是一个相对合理的**战略**。在做到`专`和`精`的同时,也会让你掌握的更快,从而更容易解出类似的题,增强自信心。
另外要介绍的就是本系列的专题系统了,一共会分为这些模块:
:::tip 专题目录
**链表篇**、**栈和队列篇**、**哈希表篇**、**二叉树篇**、**字典树**、**并查集**、**常见排序和查找算法**、**回溯算法**、**动态规划**、**贪心算法**、**LRU和LFU**、**字符串和正则篇**
:::
一共十二个模块,相信你会有不错的收获。
### 刻意练习
《**异类:不一样的成功启示录**》这本书里面谈到一万小时**刻意练习**成为高手的理论,后面也很多人谈到这个理论,我几年前就听说过,但真正体会到它的实际意义却是最近在训练算法的过程中,而且是走了很多弯路之后。可见理论到实际的过程是多么艰难。
我所理解的刻意练习,运用到算法的训练上面来,就是两点:
1. 经常性地做你不会做的题
2. 多种解法方式轮流试,最大化地挖掘一道题的价值
**刻意练习**有一个重要的观点是`走出舒适圈`。对于算法练习也是一样,为什么很多人觉得刷算法没用,那是因为他们总是在做自己熟悉的题型,用自己熟悉的方法,蹲在自己的舒适圈,这样做的再多也意义不大,但是如果总是在做自己不熟练的题型,用不一样的方法,对于自己思维的成长是相当有帮助的,所以我觉得把握经常性地做不会的题,这样做的越多越好,当然了,时间有限,我们需要根据自己的需要来取舍。
之所以我会把这个系列叫做**练习**,而不是刷题,就是因为练习的本质是`练`,而不是简单的`AC`就可以。比如下面这一题:
![](http://47.98.159.95/leetcode-js/example.jpg)
可能有经验的同学,用递归的方式很容易就解出来了。但是,你有想过如何用非递归的方式吗?如果能用非