回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
给定一个字符串S
,通过将字符串S
中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。
示例:
输入: S = "a1b2"
输出: ["a1b2", "a1B2", "A1b2", "A1B2"]
输入: S = "3z4"
输出: ["3z4", "3Z4"]
输入: S = "12345"
输出: ["12345"]
注意:
S
的长度不超过12
。S
仅由数字和字母组成。public void dfs(String pre, String S, List<String> res, int index) {
if (index == S.length())
res.add(pre);
else {
char ch = S.charAt(index);
if (!Character.isLetter(ch))
dfs(pre + ch, S, res, index + 1);
else {
// 小写字符分支
ch = Character.toLowerCase(ch);
dfs(pre + ch, S, res, index + 1);
// 大写字符分支
ch = Character.toUpperCase(ch);
dfs(pre + ch, S, res, index + 1);
}
}
}
public List<String> letterCasePermutation(String S) {
List<String> res = new LinkedList<>();
dfs("", S, res, 0);
return res;
}
// 测试
public static void main(String[] args) {
String S = "a1b";
To784To to784To = new To784To();
System.out.println(to784To.letterCasePermutation(S));
}
回溯算法和递归紧密的联系,而递归和栈有紧密关联。我们可以将递归看做一次次入栈出栈的过程,对于回溯算法则是一种参考二叉树的遍历过程中的产生的算法处理方式。在二叉树使用遍历的处理也同样用到了递归和回溯的深度优先dfs的处理方案。
递归实现1+2+3+4+5+…+100
public int test(int i) {
if(i > 0) {
int sum = i + test(i - 1);
return sum;
} else {
return 0;
}
// 最简单一个三表达式就解决了
// return i > 0 ? i + test(i - 1) : 0;
}
public static void main(String[] args) {
Test1 test1 = new Test1();
System.out.println(test1.test(5));
}
上面是的debug入栈数量,会将i-1依次放入系统栈中,当i = 0 时候开始出栈并计算 。
如果数量不是很多使用系统栈是可以的,但是如果数量太大就会出现栈溢出,那时候可以使用我们自己的定义栈实现或者java的集合作为栈进行处理。
对于这样一颗二叉树来说:
我们拿中序遍历为例
//中序遍历采用递归的方式
public void inOrder(BinaryTreeNode root){
if(null!=root){
inOrder(root.getLeft());
System.out.print(root.getData()+"\t");
inOrder(root.getRight());
}
}
执行步骤为:
对于步骤中出栈的过程,就可以看作是回溯的过程。
给定一个字符串S
,通过将字符串S
中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。这样我们脑袋中是不是就形成了一颗这样二叉树:
这样我们是不是可以通过遍历二叉树的思维实现上述的回溯算法呢!
在使用回溯算法的时候,需要多使用剪枝函数,剔除一些无用的条件,实现回溯的优化!