文章目录
17.电话号码的字母组合
class Solution {
// 组合问题: 每个子组合不允许重复就是要返回一个组合,因为返回只要求是要给一个List,List不带去重功能,所以要回溯
List<String> sumResult=new ArrayList<>();
StringBuilder stringBuilder=new StringBuilder(); //将path变为stringbuilder path.add就是stringbuilder.append path.remove(path.size()-1) 就是 stringBuilder.deleteCharAt(stringBuilder.length()-1)
public List<String> letterCombinations(String digits) {
if (digits==null || digits.length()==0) return sumResult; // 返回[]而不是null
Map<Character,String> digitDic = getLetter(digits); // 数据字典,得到n和k,可以开始组合了 n就是digitDic k就是digitDic.size
backtracking(digitDic, digits,0); // k有了 n
return sumResult;
}
public void backtracking(Map<Character,String> digitDic,String digits,int startIndex){
if (startIndex==digits.length()){ // digits.length() 就是 k,但是参数中使用digits比k要好,可以digits.charAt,k只能判断完成标志
sumResult.add(stringBuilder.toString()); // 这里i<path.size 和 i<k 是一样的,因为进入的条件就是 path.size()==k
return; // return相当于下面有一个else
}
// 从输入的digits中取出的是字符,所以初始化的时候digitDic中应该放入字符,而不是字符串
// key为字符,value为字符串,只有key为字符才可以对的上 map.get取得出 则为Map<Character,String>
String letters = digitDic.get(digits.charAt(startIndex)); // 将输入的digits变为字符串(通过数据字典)
// 然后遍历字符串
for (int i=0;i< letters.length();i++){ // 从0开始,到一个数字对应的字符为止
stringBuilder.append(letters.charAt(i)); // 每个字符遍历为止
backtracking(digitDic,digits,startIndex+1); // 因为是组合,这里是startIndex+1,深入下一个字符串
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
}
public Map<Character,String> getLetter(String digits){
char[] charArray = digits.toCharArray(); // k 就是 charArray.Length k就是每个子组合的元素个数,就是for循环层数,就是递归层数,要随时减去,要回溯
// n 就是多个大组合,getLetter中要返回这个大组合
Map<Character,String> result = new HashMap<>();
for(int i=0;i<charArray.length;i++){
if (charArray[i] == '2') result.put('2',"abc");
else if (charArray[i] == '3') result.put('3',"def");
else if (charArray[i] == '4') result.put('4',"ghi");
else if (charArray[i] == '5') result.put('5',"jkl");
else if (charArray[i] == '6') result.put('6',"mno");
else if (charArray[i] == '7') result.put('7',"pqrs");
else if (charArray[i] == '8') result.put('8',"tuv");
else if (charArray[i] == '9') result.put('9',"wxyz");
}
return result;
}
}
整个解法分为两部分,在Solution主函数中,
第一,先调用getLetter(digits);
函数建立一个Map<Character,String>类型的数据字典;
第二,调用backtracking
回溯函数
对于回溯函数:三个参数,Map<Character,String> digitDic 是传递过来的数据字典,先将数据放到了数据字典里面,然后递归的时候要取出来的,所以传递过来,其实,在递归函数中没有任何地方改变过这个数据字典,仅仅取出,所以可以作为类变量。
String digits 是刚开始的输入字符串,递归的时候要取出来的,所以传递过来,其实,在递归函数中没有任何地方改变过这个数据字典,仅仅取出,必须作为参数,无法作为类变量,因为这是Solution类的主函数的函数,只能传递
int startIndex 这个每次递归累加,必须作为参数了
对于for循环中,
stringBuilder.append(letters.charAt(i)); // 每个字符遍历为止
backtracking(digitDic,digits,startIndex+1); // 因为是组合,这里是startIndex+1,深入下一个字符串
对于两个类变量,
第一,sumResult是要作为Solution主函数的最终return返回值的,所以必须和题目要求的返回类型相同,所以是List<String>
第二,stringBuilder是取代之前的List<String>
类型的path变量的,是要放到sumResult里面的,因为是最后的返回值要求是[“ab”,“ac”]这种类型,所以用StringBuilder类型取代List<String>
类型。
分割类型(叶子节点):131.分割回文串
分割问题其实可以抽象为组合问题!
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例:
输入: “aab”
输出:
[
[“aa”,“b”],
[“a”,“a”,“b”]
]
class Solution {
List<List<String>> result=new ArrayList<>();
List<String> path =new ArrayList<>();
public void backtracking(String s,int startIndex){
if (startIndex >= s.length()){ // 游标已经到了最后一个了
result.add(new ArrayList<>(path));
return;
}
for (int i=startIndex;i<s.length();i++){ // 这里每次不是从0开始,而是从startIndex开始,否则 i+1 传递过来根本就起不到作用
if (isVerdict(s,startIndex,i)){
// subting是前闭后开,所以要i+1,才能取到第i个
path.add(s.substring(startIndex,i+1)); // 如果从startIndex到i,是一个回文,就放到path里面吧
}else{
continue;
}
backtracking(s,i+1); // 每次要移动到后面的一个
path.remove(path.size()-1);
}
}
public boolean isVerdict(String s,int start,int end){
for (int i=start,j=end;i<j;i++,j--) { // 这里只能是 i<j,可以满足s长度为奇数或偶数,不能是i<=j(等于也比较),
if (s.charAt(i)!=s.charAt(j)){
return false; // 不相等,返回false
}
}
return true; //最后return true
}
public List<List<String>> partition(String s) {
backtracking(s,0); // 给定的大组合是一个集合或者字符串,所以要从小标0开始,如果给定一个n,就是从0到n-1
return result;
}
}
分割类型(叶子节点):93.复原IP地址
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效的 IP 地址。
示例 1:
输入:s = “25525511135”
输出:[“255.255.11.135”,“255.255.111.35”]
示例 2:
输入:s = “0000”
输出:[“0.0.0.0”]
示例 3:输入:s = “1111”
输出:[“1.1.1.1”]
示例 4:输入:s = “010010”
输出:[“0.10.0.10”,“0.100.1.0”]
示例 5:输入:s = “101023”
输出:[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“101.0.2.3”]
提示:
0 <= s.length <= 3000
s 仅由数字组成
class Solution {
List<String> result = new ArrayList<>();
public void backtracking(StringBuilder stringBuilder,int startIndex,int pointNum){
if (pointNum==3){
if (isVerdict(stringBuilder,startIndex,stringBuilder.length()-1)){ // 因为isVerdict方法里面,是左闭右闭的 所以这里传入原字符串s 以及 0 - n-1
result.add(stringBuilder.toString()); // 当前判定当前的s的分割是合法的ip,就放到result里面去 这里不用path变量了,因为下面的for循环就是修改s字符串的
}
// 满足两个条件(结束条件+成功条件=成功结束条件)就添加到result里面去
// 仅仅满足结束条件,直接返回
return;
}
// 这里从starrtIndex开始,不能从0开始,否则上一次i+2传递过来就没用了
for(int i=startIndex;i<stringBuilder.length();i++){
if (isVerdict(stringBuilder,startIndex,i)){ // 从startIndex到i,左闭右闭区间
// 如果现在的划分是合法的,插入点号了
stringBuilder.insert(i+1,'.');
pointNum++;
backtracking(stringBuilder,i+2,pointNum); // 插入点号之后,想要后面那个,就是i+2位置了
stringBuilder.deleteCharAt(i+1); // 删掉掉i+1号位置,就是删除掉这个点号
pointNum--;
}else {
break;
}
}
}
private boolean isVerdict(StringBuilder s,int startIndex,int endIndex){
if (startIndex > endIndex){
return false;
}
if (s.charAt(startIndex) == '0' && startIndex!=endIndex){ //
return false;
}
int sum = 0;
for (int i=startIndex;i<=endIndex;i++){ // 左闭右闭都要
if (s.charAt(i) < '0' || s.charAt(i)>'9'){
return false; // 不是正整数去掉
}
sum = sum * 10 + (s.charAt(i) - '0');
if (sum > 255){
return false; // 字符大于255 去掉
}
}
return true;
}
public List<String> restoreIpAddresses(String s) {
StringBuilder stringBuilder=new StringBuilder(s);
backtracking(stringBuilder,0,0);
return result;
}
}