序言
现在作为前端越来越难。只会css
,js
,html
就可以当前端的日子已经过去了。现在前端要学的东西太多,技术更新太快,学了这个框架,过一阵子就被另外的框架代替,又要重新学。了解框架使用还不够,要了解框架本身原理,只会写js
不够,还要会写nodejs
。会css
不够,还要学 less
,sass
,styus
,会写pc端
不够,还要会写移动端
,小程序
。然后当你觉得你可以了,去大厂面试,发现还要会各种数据结构
,算法
…
害。路还很长,虽然头发越来越少,身体越来越虚,但还得靠着这双手养家糊口,只能硬着头皮走下去…
《前端也有算法系列》第一篇,从leetcood刷题,对于经典的题目或者算法,要坚持写题解以及博客心得,跟大家一起从算法小白慢慢成长。
今天的题目
力扣 第46题:https://leetcode-cn.com/problems/permutations/
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
回溯算法
关于回溯算法,我找到一篇好文,从经典的全排列到8皇后问题,有详细的题解。我这里就按照这篇文章的思路,一步一步的求解全排列问题。
废话不多说,直接上回溯算法框架。解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:
-
1、
路径
:也就是已经做出的选择。 -
2、
选择列表
:也就是你当前可以做的选择。 -
3、
结束条件
:也就是到达决策树底层,无法再做选择的条件。
如果你不理解这三个词语的解释,没关系,我们后面会用「全排列」和「N 皇后问题」这两个经典的回溯算法问题来帮你理解这些词语是什么意思,现在你先留着印象。
代码方面,回溯算法的框架:
result = []
function backtrack(路径, 选择列表){
if(满足结束条件)
{
result.add(路径)
return;
}
for(let i = 0 ; i < 选择列表的长度 ;i++)
{
做选择
backtrack(路径, 选择列表)
撤销选择
}
}
其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」,特别简单。
什么叫做选择和撤销选择呢,这个框架的底层原理是什么呢?下面我们就通过「全排列」这个问题来解开之前的疑惑,详细探究一下其中的奥妙!
全排列问题
我们在高中的时候就做过排列组合的数学题,我们也知道 n 个不重复的数,全排列共有 n! 个。
PS:为了简单清晰起见,我们这次讨论的全排列问题不包含重复的数字。
那么我们当时是怎么穷举全排列的呢?比方说给三个数 [1,2,3],你肯定不会无规律地乱穷举,一般是这样:
先固定第一位为 1,然后第二位可以是 2,那么第三位只能是 3;然后可以把第二位变成 3,第三位就只能