1.组合
本题可以说是组合的经典问题了。一般该题我们使用回溯算法,结合树型结构完成
根据上图再结合代码
class Solution {
LinkedList<Integer> path = new LinkedList<>();//存放组合
List<List<Integer>> res = new ArrayList<>();//输出结果
public List<List<Integer>> combine(int n, int k) {
backTrack(n,k,1);
return res;
}
void backTrack(int n,int k,int startIndex){
//终止条件,即递归结束需要输出结论
if(path.size()==k){
res.add(new ArrayList<>(path));
return;
}
//单层递归
for(int i = startIndex;i<=n;i++){
path.add(i);
backTrack(n,k,i+1);//递归,进入下一层for循环,取出下一个数
path.removeLast();//回溯
}
}
}
2.组合总和 III
两题基本一致,唯一不同的是本题新增参数sum来记录值
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
backTracking(n, k, 1, 0);
return result;
}
private void backTracking(int targetSum, int k, int startIndex, int sum) {
// // 减枝
// if (sum > targetSum) {
// return;
// }
if (path.size() == k) {
if (sum == targetSum) result.add(new ArrayList<>(path));
return;
}
// 减枝 9 - (k - path.size()) + 1
for (int i = startIndex; i <= 9; i++) {
path.add(i);
sum += i;
backTracking(targetSum, k, i + 1, sum);
//回溯
path.removeLast();
//回溯
sum -= i;
}
}
}
3.电话号码的字母组合
创建一个字符串,放入数组中。根据给的的 digits 的数字取出,映射到字符串中。然后就和上两题相似。
class Solution {
List<String> list = new ArrayList<>();
public List<String> letterCombinations(String digits) {
if(digits==null||digits.length()==0) return list;
String[] resources = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
breakTrack(digits,resources,0);
return list;
}
StringBuilder sb = new StringBuilder();
//index表示不是字母的第几个位置,而是digits的第几位(0,1,2....)
void breakTrack(String digits,String[] resources,int index){
//终止条件
if(index==digits.length()){
list.add(sb.toString());
return;
}
//将数字转换为对应的字母
String str = resources[digits.charAt(index)-'0'];//类型转换
for(int i=0;i<str.length();i++){
sb.append(str.charAt(i));
//递归
breakTrack(digits,resources,index+1);
//回溯
sb.deleteCharAt(sb.length()-1);
}
}
}
4. 组合总和
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<Integer> path = new ArrayList<>();
List<List<Integer>> ans = new ArrayList<>();
backTrack(candidates,target,path,ans,0,0);
return ans;
}
void backTrack(int[] candidates, int target,List<Integer> path,List<List<Integer>> ans,int sum,int index){
//终止条件
if(sum>target){
return;
}
if(sum==target){
ans.add(new ArrayList<>(path));
return;
}
for(int i =index;i<candidates.length;i++){
sum+=candidates[i];
path.add(candidates[i]);
candidates(candidates,target,path,ans,sum,i);
path.remove(path.size()-1);
}
}
}
5. 组合总和 II
由于不能出现重复的集合,所以本题的关键就是去重。创建一个boolean数组。同层无需去重,同树枝需要去重。
class Solution {
List<List<Integer>> lists = new ArrayList<>();
Deque<Integer> deque = new LinkedList<>();
int sum=0;
/**
去重的思路是去重同一树枝的,不去同一层的
建立一个boolea数组,若该值和前一个值不相等,说明是同一层,要去重
*/
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
//为了将重复的数字放在一起,要先排序
Arrays.sort(candidates);
boolean[] flag = new boolean[candidates.length];
backTrack(candidates,target,flag,0);
return lists;
}
public void backTrack(int[] candidates, int target,boolean[] flag,int index){
//终止条件
if(sum==target){
lists.add(new ArrayList<>(deque));
return;
}
for(int i=index;i<candidates.length&&sum+candidates[i]<=target;i++){
//去重,判断是同层值等还是同枝值等
if(i>0&&candidates[i]==candidates[i-1]&&!flag[i-1]){
continue;
}
//同层
deque.push(candidates[i]);
sum+=candidates[i];
flag[i]=true;
//同枝
backTrack(candidates,target,flag,i+1);
int temp = deque.pop();
flag[i]=false;
sum-=temp;
}
}
}