36. 有效的数
37. 解数独
38. 外观数列
递归
我们要求第n个外观数列,一定要获得第n-1个外观数列
public String countAndSay(int n) {
if(n == 1){
return "1";
}
String last = countAndSay(n - 1);//得到上一行的字符串
return getNextString(last);//输出当前行的字符串
}
public String getNextString(String last){
if(last.length() == 0) return "";
//得到第 1 个字符重复的次数
int num = getRepeatNum(last);
// 次数 + 当前字符 + 其余的字符串的情况
return num + "" + last.charAt(0) + getNextString(last.substring(num));
}
//得到字符 string[0] 的重复个数,例如 "111221" 返回 3
private int getRepeatNum(String string) {
int count = 1;
char same = string.charAt(0);
for (int i = 1; i < string.length(); i++) {
if (same == string.charAt(i)) {
count++;
} else {
break;
}
}
return count;
}
优化:递归和StringBuilder结合
public String countAndSay(int n) {
if (n == 1) {
return "1";
}
StringBuffer res = new StringBuffer();
String str = countAndSay(n - 1);
int length = str.length();
int a = 0;
for (int i = 1; i < length + 1; i++) {
if (i == length) {
return res.append(i - a).append(str.charAt(a)).toString();
} else if (str.charAt(i) != str.charAt(a) ) {
res.append(i - a).append(str.charAt(a));
a = i;
}
}
return res.toString();
}
39. 组合总和(考点)
题目:给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。其中candidates 中的数字可以无限制重复被选取。
回溯
每个数字可以用多次,所以backtrace里面可以这样写,因为每个数字都可以使用无数次,所以递归还可以从当前元素开始
for(int i=0;i<candidates.length;i++){
path.add(candidates[i]);
//因为每个数字都可以使用无数次,所以递归还可以从当前元素开始
backtrace(res,path,candidates,0,remain-candidates[i]);
path.remove(path.size()-1);
}
每次递归我们都是从 0 开始,所有数字都遍历一遍。所以会出现重复的组合。改进一下,只需加一个 start 变量即可
for(int i=start;i<candidates.length;i++){
path.add(candidates[i]);
backtrace(res,path,candidates,i,remain-candidates[i]);
path.remove(path.size()-1);
}
完整代码
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<>();
int len=candidates.length;
if(len==0) return res;
List<Integer> path=new ArrayList<>();
backtrace(res,path,candidates,0,target);
return res;
}
public void backtrace(List<List<Integer>> res,List<Integer> path,int[] candidates, int start,int remain){
if(remain<0) return;
if(remain==0) res.add(new ArrayList<>(path));
//每次递归的时候从start开始,而不是0开始,后者会产生很多重复项
for(int i=start;i<candidates.length;i++){
path.add(candidates[i]);
backtrace(res,path,candidates,i,remain-candidates[i]);
path.remove(path.size()-1);
}
}
40. 组合总和 II(考点)
这道题和39题不同的是,这道题数组中的数字只能被选择一次
解题思想还是一样的,我们要做的是怎么过滤掉重复的,首先可以对原数组进行排序,排序之后相同的肯定是挨着的,if(candidates[i] == candidates[i - 1])我们就过滤掉candidates[i],我们就仿照39. 组合总和来写下这道题的答案
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<>();
int len=candidates.length;
if(len==0) return res;
Arrays.sort(candidates);
List<Integer> path=new ArrayList<>();
backtrace(res,path,candidates,0,target);
return res;
}
public void backtrace(List<List<Integer>> res,List<Integer> path,int[] candidates, int start,int remain){
if(remain<0) return;
if(remain==0) res.add(new ArrayList<>(path));
//每次递归的时候从start开始,而不是0开始,后者会产生很多重复项
for(int i=start;i<candidates.length;i++){
//这里去重,注意i的范围
if(i>start&&candidates[i]==candidates[i-1]){
continue;
}
path.add(candidates[i]);
//这里改成i+1,因为一个数只能用一次
backtrace(res,path,candidates,i+1,remain-candidates[i]);
path.remove(path.size()-1);
}
}