原题目链接:46. 全排列
题目描述:
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
做题思路:
我们可以把数组的每个元素都想象成一个节点,然后进行排列,会发现是一颗二叉树的形状,如图所示
- 我们先对数组的每个元素进行状态设置,例如这个元素被使用过吗?因为我们发现,不能出现重复的元素,例如第一层已经用过了1,那么第二层就不能再添加1。
- 采用一种取法从数组中取出每个元素,然后直到数组的元素取完了,没得了,就回溯,采用另外一种取法从数组中取出每个元素,那么这个过程需要注意一个点,记得还原现场,就类似于月光宝盒,当你发现一种取法结束了,可以打开月光宝盒穿越到开始取的时候,然后进行另一种取法,那么在穿越回开始取的时候,之前那些对元素做的操作,就要还原成原本的样子,也就是取消那些对元素做的操作。
- 每遍历到一个元素,如果这个元素没有被使用过,就入栈,并且更新一下该元素的状态,显示是被使用过的,如果递归的深度是数组的长度,说明已经到最底了,这时候需要进行回溯的操作,以此类推。
废话不多说,直接上代码,为了让各位看官更能清晰理解,我的代码写得不精简,我的代码里加了大量的注释,相信各位看官可以理解,如果我有些没写清楚或者写错的,可以评论区或者私信我喔
class Solution {
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
//这个集合是用来保存答案的
List<List<Integer>> res = new ArrayList<>();
//如果数组的长度为0,就直接返回空的集合
if(len == 0) return res;
//创建一个栈来保存从根节点到节点的路径
Deque<Integer> path = new ArrayDeque<>();
//判断这个数组元素是否已经被用过
boolean[] used = new boolean[len];
//这个0代表从第0层开始排列
dfs(nums, 0,path, used, res);
return res;
}
private void dfs(int[] nums, int dept, Deque<Integer> path, boolean[] used, List<List<Integer>> res){
if(dept == nums.length){ //当深度达到数组长度的时候,说明已经到达最底
//如果这个直接add(path),那么导致的后果就是res输出的[][][][][][]
//因为path是存储根节点到节点的路径,那么在进行回溯的时候
//会不断的向上找,由于回溯要还原现场,也就说之前入栈的元素将会全部弹栈,达到原本的样子
//所以这里我的处理方法是,直接新建一个动态数组放进res集合,就相当于储存了一个当前ArrayList的快照
res.add(new ArrayList<>(path));
return;
}
//开始遍历数组的元素
for(int i = 0; i < nums.length; i++){
//如果这个元素已经被使用了,就跳过
if(used[i]) continue;
//如果没有被使用过,就改变这个元素的状态
path.addLast(nums[i]); //注意这里是加入栈底,因为栈是从栈顶弹出
used[i] = true;
//递归调用下一层
dfs(nums, dept + 1,path, used, res);
//回溯
path.removeLast(); //从栈底删除
used[i] = false; //恢复原本的状态
}
}
}