本系列使用IDEA+LEETCODE EDITOR插件,题目描述统一英文。
题目链接
一、题目描述:
//Given an array nums of distinct integers, return all the possible permutations
//. You can return the answer in any order.
//
//
// Example 1:
// Input: nums = [1,2,3]
//Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
// Example 2:
// Input: nums = [0,1]
//Output: [[0,1],[1,0]]
// Example 3:
// Input: nums = [1]
//Output: [[1]]
//
//
// Constraints:
//
//
// 1 <= nums.length <= 6
// -10 <= nums[i] <= 10
// All the integers of nums are unique.
二、思路分析:
在之前的这道题
LC78 同样是回溯也有优劣之分的全部子集问题|刷题打卡中,一开始的解我在每次回溯的递归中使用了循环,导致性能很差,修改成每次回溯的递归只最多加入一个值到结果集的方式。
今天我们看一道同样在回溯的递归中使用循环会导致性能非常差,最后优化成高效的题目。
题目描述比较简单,就是一个数组的全排列,也没存在什么坑人的地方,这是比较幸福的题代表你基本都能写出来就是性能优化上要下功夫。
当发现题目描述很清晰也没有什么坑的时候,你应该想到这道题对所有人都是容易写出解的,考官看的是谁能更好的进行优化,能想到这些,这才是本期的重点。
三、AC 代码:
首先看使用了循环的低效代码:
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
backTrack(0,new ArrayList<Integer>(),nums);
return res;
}
void backTrack(int currentLength,List<Integer> contains,int[] nums){
if(nums.length == currentLength){
if (!res.contains(contains)){
res.add(contains);
}
return;
}
for (int i = 0; i < nums.length; i++) {
if(!contains.contains(nums[i])){
List<Integer> tmp = new ArrayList<>(contains);
tmp.add(nums[i]);
backTrack(currentLength +1,tmp,nums);
}
}
}
}
解答成功:
执行耗时:11 ms,击败了6.00% 的Java用户
内存消耗:38.6 MB,击败了75.64% 的Java用户
可以看到性能惨不忍睹,哪怕使用了剪枝。因为这个剪枝是针对一次递归的局部剪枝,并不是全局的,导致大量的重复计算。
开始优化:
我们发现上面解中递归完成的条件判断参数是多余的,可以直接用临时结果集的长度判断,然后全局递归需要一个全局备忘录,记录哪些数字已经被使用了,
List<List<Integer>> res = new ArrayList<>();
boolean[] used = null;
public List<List<Integer>> permute(int[] nums) {
used = new boolean[nums.length];
backTrack(new ArrayList<>(), nums);
return res;
}
void backTrack(List<Integer> contains,int[] nums) {
if (contains.size() == nums.length) {
res.add(new ArrayList<>(contains));
return;
}
for (int i = 0; i < nums.length; ++i) {
if (!used[i]) {
used[i] = true;
contains.add(nums[i]);
backTrack(contains, nums);
contains.remove(contains.size() - 1);
used[i] = false;
}
}
}
解答成功:
执行耗时:1 ms,击败了96.67% 的Java用户
内存消耗:38.4 MB,击败了96.46% 的Java用户
四、总结:
- 题目描述简单的题,要思考如何优化。
- 进行好剪枝,回溯里一样可以使用循环。
- 逢递归,必剪枝。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情