LC排列组合类问题总结
最最常见的不过四种:
给定一个无重复/有重复的数组,输出其所有的排列/组合
解决方法大致上也是构建递归树 进行合理剪枝
LC46 无重复元素的排列
LC47 有重复元素的排列
LC77 无重复元素的组合
subset问题:
LC78 无重复元素的子集
LC90 有重复元素的子集
下面是代码:
public static List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
helper(res, new ArrayList<>(), nums);
return res;
}
public static void helper(List<List<Integer>> res, List<Integer> list, int[] nums) { //递归函数有三个参数 总的res(nested list) res的基本组成单位
if (list.size() == nums.length) { //触底警告!
res.add(new ArrayList<>(list));
return;
}
for (int i = 0; i < nums.length; i++) { //for loop递归 为什么要用for 因为每一层递归我们都要试一遍所有的 将没加过的加进去
if (list.contains(nums[i])) continue; // O(n)
list.add(nums[i]);//前面是一直再加 也就是符合一直在push 进栈
helper(res, list, nums);
list.remove(list.size() - 1);//后面的弹出是在出栈,为什么要出栈,是因为我们在这个过程中重复利用了list这个变量
}
}
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
Arrays.sort(nums);//没要求时间复杂度 因此要进行预排序
helper(res, new ArrayList<>(), nums, new boolean[nums.length]);
return res;
}
public void helper(List<List<Integer>> res, List<Integer> list, int[] nums, boolean[] used) {
if (list.size() == nums.length) {
res.add(new ArrayList<>(list));
}
for (int i = 0; i < nums.length; i++) {
if (used[i] || i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;//如果i被标记为true 或者
used[i] = true;//添加前标记一下
list.add(nums[i]);
helper(res, list, nums, used);
used[i] = false;
list.remove(list.size() - 1);
}
}
//组合的输入与之前略有不同 但是本质都一样
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
helper(res, new ArrayList<>(), n, k, 1);
return res;
}
public void helper(List<List<Integer>> res, List<Integer> list, int n, int k, int start) {
if (k == 0) {
res.add(new ArrayList<>(list));//因为规定了list是个List 所以要转换一下?list为什么不可以直接定义ArrayList呢?
return;
}
for (int i = start; i <= n; i++) { //之前的排列 这儿每次都是从1开始到n 然后在内部剪掉已经存在的枝,现在的组合因为是按照顺序来的 因此每一次都要进行不一样的剪枝
list.add(i);
helper(res, list, n, k - 1, i + 1);
list.remove(list.size() - 1);//返回上一层
}
}
public static List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
helper(res, new ArrayList<>(), nums, 0);
return res;
}
public static void helper(List<List<Integer>> res, List<Integer> list, int[] nums, int index) {
res.add(new ArrayList<>(list));
for (int i = index; i < nums.length; i++) { //排列组合算法的核心还保留着
list.add(nums[i]);
helper(res, list, nums, i + 1);
list.remove(list.size() - 1);
}
}
public List<List<Integer>> subsets2(int[] nums) {
int length = nums.length;
int size = (int) Math.pow(2, length);
List<List<Integer>> subsets = new ArrayList<>();
for (int i = 0; i < size; i++) {
subsets.add(new ArrayList<>());//首先初始化一个二维数组subset,初始化长度为2的length次方 因为2的length次方就是subset的个数
}
for (int i = 0; i < length; i++) { //
for (int j = 0; j < size; j++) { //按照顺序写入subset
if ((j >> i & 1) == 1) {//只有j>>i是奇数 这个if语句才成立 虽然不明白原理 但是跑一下case 是对的
subsets.get(j).add(nums[i]);
}
}
}
return subsets;
}
public static List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) return res;
Arrays.sort(nums);
helper(res, new ArrayList<>(), nums, 0);
return res;
}
public static void helper(List<List<Integer>> res, List<Integer> list, int[] nums, int index) {
res.add(new ArrayList<>(list));
for (int i = index; i < nums.length; i++) {
if (i != index && nums[i] == nums[i - 1]) continue;
list.add(nums[i]);
helper(res, list, nums, i + 1);
list.remove(list.size() - 1);
}
}
此外 还有LC93

浙公网安备 33010602011771号