来源:码海
作者:子非鱼
上一篇「【超详细】一文学会递归解题」一文颇受大家好评,各大号纷纷转载,让笔者颇感欣慰,不过笔者注意到后台有读者有如下反馈
确实,相信很多人(包括我自己)都有类似的感慨,对某个知识点,看确实是看懂了,但如果真的再用同样的套路再去解一些带有同样解题思路,但稍加变形的题,往往会束手无策。对这种情况有啥好的解决办法吗?
除了勤加练习,还有一良策!
鲁迅先生说:如果学习算法,最好一段时间内只刷某种算法思想或某种数据结构的题,啥意思呢?比如说你上次学了递归,那就持续找递归的题来刷,学了链表,这段时间就专门刷链表的题,千万不可今天刷递归,明天刷动态规划,后天又开始学习贪心算法。。。新手最怕的就是以为自己懂了,浅尝辄止,这是新手的大忌!一定要对同一类型的题穷追猛打,形成肌肉记忆,这样之后再碰到同一类型的题就会条件反射地一看:哦,这题用 xxx 思想应该可以靠谱。
言归正转,排列组合是面试中的热门考点 因为看似简单的排列组合可以有挺多的变形,根据变形,难度可以逐渐递增,而且排列组合本身有挺多的解法,能很好地区分一个侯选者的算法水平,排列组合如果用递归挺不容易理解的(反正笔者一开始看了好几遍代码愣是没看懂),之后我会教大家如何用一种非常简单地方式来理解排列组合的递归,这也是写本文的根本目的
接下来我们看看如何用 「递归四步曲」来解排列组合,本文会从以下几个方面来讲解排列组合
- 什么是排列
- 排列的常用解法
- 什么是组合
- 组合递归解法
- 面试中排列组合的一些变形
什么是排列
排列的定义:从n个不同元素中,任取 m (m≤n,m与n均为自然数,下同)个不同的元素按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列;从n个不同元素中取出m(m≤n)个元素的所有排列的个数,叫做从n个不同元素中取出m个元素的排列数,当 n = m 时,我们称这样的排列为全排列
看到这个公式,大家是不是回忆起了高中的排列公式啦
我们重新温习一下,以 1, 2, 3 这三个数字的全排列有多少种呢。
第一位我们可以选择 3 个数字,由于第二位不能与第一位相等,所以第二位只能选 2 个数字,第一,第二位既然选完了,那么第三位就只有 1 个数字可选了,所以总共有 3 x 2 x 1 = 6 种排列。
既然知道了什么是全排列,那我们来看看怎么用程序来打印全排列的所有情况:求 数字 1 到 n (n < 10) 的全排列
排列的常用解法
这道题如果暂时没什么头绪,我们看看能否用最简单的方式来实现全排列,什么是最简单的方式,暴力穷举法!
暴力穷举法
大家仔细看上文中 1,2 ,3 的全排列,就是把所有情况全部列举出来了,所以我们用暴力穷举法怎么解呢,对每一位的每种情况都遍历出来组成所有的排列,再剔除重复的排列,就是我们要的全排列了
/**
* 求数字第 1 到 n 的全排列
*/
public void permutation(int n) {
for(int i = 1; i < n + 1; i ++) {
for(int j = 1; j < n + 1; j ++) {
for(int k = 1; k < n + 1; k ++) {
if (i != j && i != k && j != k) {
System.out.println(i + j + k);
}
}
}
}
}
时间复杂度是多少呢,做了三次循环,很显然是
很多人一看时间复杂度这么高,多数都会嗤之以鼻,但是要我说,得看场景,就这题来说用暴力穷举法完全没问题,n 最大才 9 啊,总共也才循环了 9^3