1. 题目描述
2. 解题思路
对于此类排列组合的问题,我们其实是可以找到一定规律的,下面来观察一下:
- 数组是 [1],结果是 [[1]]
- 数组是 [1,2],结果是 [[1,2],[2,1]]
- 数组是 [1,2,3],结果是 [[1,2,3],[1,3,2],[3,1,2],[2,1,3],[2,3,1],[3,1,2]]
有没有发现什么规律?数组 [1,2] 比数组 [1] 多了一个2,那么答案就是在 [1] 的左边或者右边插入一个2;数组 [1,2,3] 比数组 [1,2] 多了一个3,那么答案就是在 [1,2]或[2,1] 的左边或者右边或者中间插入一个3。换言之,假如我们想要求数组 [a,b,c,…,x] 的答案,我们是可以从数组 [a] 开始,到数组 [a,b] ,到数组 [a,b,c] …一步一步插入新的元素构建最终的答案。
除了找规律以外还有什么解法吗?假如你做过【LeetCode - Java】17. 电话号码的字母组合(中等)和【LeetCode - Java】22. 括号生成(中等)这类型字符串生成的题目,你应该就可以想到DFS回溯,这里需要使用两个表来维护候选数与候选答案。下面这样一棵树的叶子节点路径就是我们所需要的答案了。
3. 代码实现
3.1 模拟插入法
public List<List<Integer>> permute(int[] nums) {
ArrayList<List<Integer>> lists = new ArrayList<>();
lists.add(Arrays.asList(nums[0]));
int length = 1;
for (int i = 1; i < nums.length; i++) {
int count = 0;
for (int j = 0; j < length; j++) {
List<Integer> remove = lists.remove(0);
for (int k = 0; k < i + 1; k++) {
ArrayList<Integer> temp = new ArrayList<>(remove);
temp.add(k, nums[i]);
lists.add(temp);
count++;
}
}
length = count;
}
return lists;
}
3.2 DFS回溯
ArrayList<List<Integer>> lists = new ArrayList<>();
ArrayList<Integer> numList = new ArrayList<>();
ArrayList<Integer> ansList = new ArrayList<>();
int length;
public List<List<Integer>> permute(int[] nums) {
length = nums.length;
for (int num : nums) {
numList.add(num);
}
build(0);
return lists;
}
public void build(int index) {
if (numList.isEmpty()) {
lists.add(new ArrayList<>(ansList));
return;
}
for (int i = 0; i < length-index; i++) {
ansList.add(numList.remove(0));
build(index + 1);
numList.add(ansList.remove(index));
}
}
3.3 对比
模拟插入和DFS回溯的时间复杂度都是O(n * n!),其中第一个n代表的是外层大循环,对模拟插入而言则是代表现在应该插入下标为i(i<n)的数(也可以理解为现在待插入的答案长度为i),对DFS回溯而言代表着现在候选答案中第一个数是下标为i(i<n)的数;而 n! 代表长度为 i 的全排列有多少个(n个数的全排列个数就是n!),在模拟插入中是以两层循环呈现,在DFS回溯中则是以栈的深度与栈的回溯呈现。
在模拟插入中构建了1!+2!+…+n-1!个过程答案,因此其空间复杂度也是O(n * n!);在DFS回溯中使用了两个表来维护候选数与候选答案,其长度都是n,一共2n,除此以外还有用于递归的栈,深度为n,加起来一共为3n,因此其空间复杂度是O(n)。
最后要提醒一下,在使用ArrayList.add()
的时候,假如你add
的对象是一个list
,一定要记得这是一个地址!是一个地址!是一个地址! 假如你在add
之后又对那个list
操作了,其状态会同步,想要不变必须要new
一个List
出来再add
!!!切记!!!排bug的教训!!!
错误做法:
ArrayList<List<Integer>> lists = new ArrayList<>();
ArrayList<Integer> ansList = new ArrayList<>();
ansList.add(1);
ansList.add(2);
lists.add(ansList);
ansList.add(3);
System.out.println(lists);
/* 输出为[[1,2,3]] */
正确做法:
ArrayList<List<Integer>> lists = new ArrayList<>();
ArrayList<Integer> ansList = new ArrayList<>();
ansList.add(1);
ansList.add(2);
lists.add(new ArrayList<>(ansList));
ansList.add(3);
System.out.println(lists);
/* 输出为[[1,2]] */