「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。
题目:给定一个无重复的数组nums,返回其所有可能的全排列。
解题思路
本题参考题解,成功率七十多,我是废物!题解看了半天有了点点感觉,下面介绍一下:
本题解决方法为使用回溯法,回溯实际上就是一种暴力解法,通过一步步试错,找到最终正确的答案。本题要求找到一个数组的全排列,一种很直观的思想就是从前往后多重遍历数组,使用一个列表来储存每次遍历的合适的值,合适的值就是指那些不在当前列表的,最终当列表的长度为当前数组的长度时,这个解就满足。如何获取一个合适的值很简单,可以新增一个和当前数组等长度的visited数组,数组i上的元素代表第i个元素是否已经在列表中,在每次遍历的时候,我们可以查询当前元素在visited数组中的情况来选择加入还是跳过。
我们可以使用递归来解决问题,借用leetcode题解中的一张图:
我们首先设置全局变量list来作为最终结果,使用局部变量arr列表存储中间过程的结果,初始中间结果为空,创建boolean数组来存储数组元素的使用情况,之后调用回溯函数,回溯函数的过程为:
- 判断当前arr列表长度,如果长度等于数组长度则全局变量add当前列表,程序结束。
- 遍历数组,判断数组中每个元素的使用情况,如果未使用则add进局部变量,之后设置已使用,调用回溯算法,之后删除列表最后一个元素,设置未使用,函数回溯。
- 递归调用上述过程即可。
上面那个图是精髓!
代码如下:
private ArrayList<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
int n = nums.length;
boolean[] visited = new boolean[n];
ArrayList<Integer> arr = new ArrayList<>();
backtrace(arr, visited, nums);
return result;
}
public void backtrace(ArrayList<Integer> arr, boolean[] visited, int[] nums){
if(arr.size()==nums.length){
result.add(new ArrayList<>(arr));
}
for(int i=0;i<nums.length;i++){
if(!visited[i]){
arr.add(nums[i]);
visited[i] = true;
backtrace(arr, visited, nums);
arr.remove(arr.size()-1);
visited[i] = false;
}
}
}
上述代码存在一个需要注意的地方:
result.add(new ArrayList<>(arr));
此处如果写成:
result.add(arr);
则会得到所有结果的空列表。这是因为Java中列表存储的是内存地址,当向一个列表add另一个列表的时候,add的是那个列表的引用,当对列表进行操作的时候,已经被add的列表也会受影响。而回溯则会返回上一个结点,因此会得到全部为空的结果。该算法时间复杂度和空间复杂度都为。