注意组合类和子集类问题都不需要考虑顺序,排列类问题需要考虑顺序。
组合和子集类问题:
- 求总和等于target,就要判断target==0;
- i=start,结果中不能含有重复元素,i+1,结果可以含有重复元素,i(子集问题不能含重复元素)
- 如果给定的数中有重复元素,需要Arrays.sort();判断if (i > start && nums[i] == nums[i - 1]) {continue;}
全排列类问题:
- 判断list.size()==nums.length;
- i=0,结果肯定不能含有重复元素
- 没有重复元素用contains
- 如果有给定的数中有重复元素,需要Arrays.sort();需要used数组;判断if (i > 0 && nums[i] == nums[i - 1] && used[i - 1]) {continue;}
1.组合总和1(数组无重复元素,数字可以被重复选取)
public class T01_CombinationSum {
//数组中无重复元素
public List<List<Integer>> combinationSum(int[] candidates, int target) {
ArrayList<List<Integer>> res = new ArrayList<>();
if (candidates == null || candidates.length == 0) {
return res;
}
helper(res, new ArrayList<Integer>(), candidates, target, 0);
return res;
}
private void helper(List<List<Integer>> res, List<Integer> list, int[] candidates, int target, int start) {
if (target == 0) {
res.add(new ArrayList<>(list));
return;
}
if (target < 0) {
return;
}
for (int i = start; i < candidates.length; i++) {
list.add(candidates[i]);
helper(res, list, candidates, target - candidates[i], i);//可以选取重复元素
// helper(res, list, candidates, target - candidates[i], i + 1);//不可以选取重复元素
list.remove(list.size() - 1);
}
}
}
2.组合总和2(数组可能有重复元素,数字不可以被重复选取)
public class T02_CombinationSum2 {
//数组中不知有无重复元素,数字不可被重复选取
//这个代码同样适用于无重复元素,数字可被重复选取的,只需要将倒数第二行的i+1变成i即可
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
ArrayList<List<Integer>> res = new ArrayList<>();
if (candidates == null || candidates.length == 0) {
return res;
}
Arrays.sort(candidates);//不同
helper(res, new ArrayList<Integer>(), candidates, target, 0);
return res;
}
private void helper(List<List<Integer>> res, List<Integer> list, int[] candidates, int target, int start) {
if (target == 0) {
res.add(new ArrayList<>(list));
return;
}
if (target < 0) {
return;
}
for (int i = start; i < candidates.length; i++) {
if (i > start && candidates[i] == candidates[i - 1]) {//不同
continue;
}
list.add(candidates[i]);
helper(res, list, candidates, target - candidates[i], i + 1);//不可以选取重复元素
list.remove(list.size() - 1);
}
}
}
3.子集1(数组无重复元素)
子集类问题和组合总和类问题的区别在于少了target的判断,并且同一个元素不会选取两次
public class T03_Subsets {
public List<List<Integer>> subsets(int[] nums) {
//不包含重复元素
ArrayList<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) {
return res;
}
helper(res, new ArrayList<>(), nums, 0);
return res;
}
private void helper(List<List<Integer>> res, List<Integer> list, int[] nums, int start) {
res.add(new ArrayList<>(list));
for (int i = start; i < nums.length; i++) {
list.add(nums[i]);
helper(res, list, nums, i + 1);//子集,同一个元素不会被选两次
list.remove(list.size() - 1);
}
}
}
4.子集2(数组可能有重复元素)
public class T04_Subsets2 {
public List<List<Integer>> subsetsWithDup(int[] nums) {
//可能包含重复元素,这个代码也适用于无重复元素的
ArrayList<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) {
return res;
}
Arrays.sort(nums);
helper(res, new ArrayList<>(), nums, 0);
return res;
}
private void helper(List<List<Integer>> res, List<Integer> list, int[] nums, int start) {
res.add(new ArrayList<>(list));
for (int i = start; i < nums.length; i++) {
if (i > start && nums[i] == nums[i - 1]) {
continue;
}
list.add(nums[i]);
helper(res, list, nums, i + 1);//子集,同一个元素不会被选两次
list.remove(list.size() - 1);
}
}
}
5.组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。(和子集问题很像,区别是限制了长度k,同样也是不可以选取重复元素)
public class T05_Combine {
public List<List<Integer>> combine(int n, int k) {
ArrayList<List<Integer>> res = new ArrayList<>();
if (n < 1 || k > n) {
return res;
}
helper(res, new ArrayList<>(), n, k, 1);
return res;
}
private void helper(List<List<Integer>> res, List<Integer> list, int n, int k, int start) {
if (list.size() == k) {
res.add(new ArrayList<>(list));
return;
}
for (int i = start; i <= n; i++) {
list.add(i);
helper(res, list, n, k, i + 1);//组合,肯定也是不能选取重复元素的
list.remove(list.size() - 1);
}
}
}
6.全排列1(没有重复元素)
给定一个没有重复数字的序列,返回其所有可能的全排列。
(没有重复元素那就用contains方法就可以)
(全排列类问题总是从0开始遍历,不需要start标记,需要判断长度)
public class T06_Permute {
public List<List<Integer>> permute(int[] nums) {
ArrayList<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) {
return res;
}
helper(res, new ArrayList<>(), nums);
return res;
}
private void helper(List<List<Integer>> res, List<Integer> list, int[] nums) {
if (list.size() == nums.length) {
res.add(new ArrayList<>(list));
return;
}
for (int i = 0; i < nums.length; i++) {
if (list.contains(nums[i])) {//不同
continue;
}
list.add(nums[i]);
helper(res, list, nums);
list.remove(list.size() - 1);
}
}
}
7.全排列2(可能有重复元素)
给定一个可能包含重复数字的序列,返回所有不重复的全排列。(也适用于无重复元素的)
(有重复元素,需要用一个used数组判断元素是否被使用过)
public class T07_Permute2 {
public List<List<Integer>> permute2(int[] nums) {
ArrayList<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;
}
private void helper(List<List<Integer>> res, List<Integer> list, int[] nums, boolean[] used) {
if (list.size() == nums.length) {
res.add(new ArrayList<>(list));
return;
}
for (int i = 0; i < nums.length; i++) {
//used数组用来区分数字是否被使用过
if (used[i]) {//不同
continue;
}
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1]) {//不同
continue;
}
used[i] = true;//不同
list.add(nums[i]);
helper(res, list, nums, used);
list.remove(list.size() - 1);
used[i] = false;//不同
}
}
}
8.第k个排列
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
给定 n 和 k,返回第 k 个排列。
和全排列问题很相似,区别是多加了size等于k时停止,并且把结果拼接成字符串
public class T08_getKthPermutation {
public String getPermutation(int n, int k) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
helper(res, new ArrayList<>(), n, k);
StringBuilder str = new StringBuilder();//最后将list拼接成字符串
ArrayList<Integer> l = res.get(k - 1);
for (int i = 0; i < l.size(); i++) {
str.append(l.get(i));
}
return str.toString();
}
private void helper(ArrayList<ArrayList<Integer>> res, ArrayList<Integer> list, int n, int k) {
if (res.size() == k) {
return;
}
if (list.size() == n) {
res.add(new ArrayList<>(list));
return;
}
for (int i = 1; i <= n; i++) {
if (list.contains(i)) {
continue;
}
list.add(i);
helper(res, list, n, k);
list.remove(list.size() - 1);
}
}
}