46. 全排列
这里要说明一点:排列是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,它和子集以及组合是不同的。
注意:这里题目中说明了数组中不包含重复数字。
思路一:使用used数组来标记已经选择的元素
思路分析:使用一个额外的数组used来记录的是此时数组中的哪些元素已经在path中了,因为一个排列里一个元素只能使用一次。
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
int[] used;
public List<List<Integer>> permute(int[] nums) {
used = new int[nums.length];
backtrack(nums);
return result;
}
public void backtrack(int[] nums) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i] == 1) {
continue;
}
path.add(nums[i]);
used[i] = 1;
permuteHelper(nums);
used[i] = 0;
path.remove(path.size() - 1);
}
}
}
思路二:直接通过判断path中是否存在数字,排除已经选择的数字
思路分析:因为题目中说了 nums 不包含重复数字,所以我们可以利用 ArrayList 中的 contains()方法来判断改数字是否已经存在在path中了。
// 解法2:通过判断path中是否存在数字,排除已经选择的数字
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) {
if (nums.length == 0) return result;
backtrack(nums, path);
return result;
}
public void backtrack(int[] nums, LinkedList<Integer> path) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
}
for (int i =0; i < nums.length; i++) {
// 如果path中已有,则跳过
if (path.contains(nums[i])) {
continue;
}
path.add(nums[i]);
backtrack(nums, path);
path.removeLast();
}
}
}
47. 全排列 II (重要)
思路分析:本题是在上一题的基础上进一步提升难度,即数组中可以包含重复数字。所以关键就在于如何去重:
- 我们首先对数组进行排序,然后用一个额外数组used标记数组中的数是否使用过;
- 如果出现重复数字,即
nums[i] == nums[i - 1]
;然后利用 used 数组判断 nums[i - 1] 是否已经被使用过,即used[i - 1] == false
(注意这里是 used[i - 1] == false,而不是 used[i - 1] == true):if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) { continue; }
代码如下:
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
used = new boolean[nums.length];
permuteUniqueHelper(nums);
return result;
}
public void permuteUniqueHelper(int[] nums) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
// 同一数层去重
// used[i - 1] == true,说明同一树支nums[i - 1]使用过
// used[i - 1] == false,说明同一树层nums[i - 1]使用过 ==》 这里对同一数层进行去重
// 如果同一树层nums[i - 1]使用过则直接跳过
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
continue;
}
if (used[i] == false) {
path.add(nums[i]);
used[i] = true;
permuteUniqueHelper(nums);
used[i] = false;
path.remove(path.size()-1);
}
}
}
}