这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战
一、全排列
例如给你一个数组【1,2,3】,然后排列成[123][132][213][231][312][321]
最暴力的方法就是使用三重循环实现,但是如果原始数组更多,8个数组呢就会很不好实现,一般这种主要是突出树形结构,乍一看好像跟树没有关系,但是可以用树的思想,一般使用递归/迭代+回溯法实现。
实现全排列可以有几种方法:
- 字典序法:给定字符集中的字符规定了先后关系,然后规定两个全排列的先后顺序是从左到右逐个比较
- 递增进位制:排列求出中介数,这里的中介数由排列的数字决定的,中介数中各位的下标与排列中的数字一致,最低位是逢2进1
- 递减进位制:有了上面的递增就有递减,就是把递增是进位翻转
- 临位对换法:下一个排列总是上一个排列某相邻的两位对换得到的
思路:
- 可以探索所有的方式例如从1开始,把所有1开始的排列实现,然后2,最后3这样就类似树
- 知道数据结构是树,思路就可以用过回溯,因为是全排列不知道一共有多少种,只能一个一个试,并且全排列只要摆出来就行,把1,2,3摆出来还要重试。
- 开始之后从1开始往下找到2再找到3,到3结束后这条线就结束了,再往上重新找找到3直到没有为止。
结论:
采用树的结构表示全排列生成算法就是递减进位制数,树的每个节点的值都是从父节点继承而来的,所以不同的父节点生成的子节点数字都不同,这样保证每个子节点生成排列不会重复。
二、实战:leetCode第46题
题目:给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
//辅助函数
function backtrack(list,temp,nums) {
//递归的终止条件
if(temp.length == nums.length) {
return list.push([...temp]);
}
for(let i = 0;i<nums.length;i++) {
if(temp.includes(nums[i])) continue
temp.push(nums[i]);
backtrack(list,temp,nums);
temp.pop();
}
}
var permute = function(nums) {
let list = [];
backtrack(list,[],nums);
return list;
}
思路:
- temp是用来临时存储的list中的数字,例如[1,2,3]中的1就是入栈出栈的一个变量
- 循环数组,然后使用includes判断是否包含
- 再把数字存到数组中,然后递归,再把temp中的数字删掉执行出栈
- 加上条件返回数组,递归一定要有终止条件。
- 最后再返回一个二维数组
当然啦,这段代码不是最优解,代码可以加上优化比如把includes替换成其他的等等