回溯模板
一、算法流程
回溯就是穷举法
穷举的话有两个方向一个是深度就是利用递归函数,另一个是横向遍历利用for循环
运行流程:
终止条件控制我们什么时候返回,然后跳回本层,相对的就是执行完了本层的backtraceing()
2、弹出一个数值我们插入的,
3、继续for循环,等到本层都遍历完了,就会由于for循环结束。跳到上一层
2、模板
void backtracking(参数)
{
if (终止条件) //等我们取到足够就数字,就使用return返回本层
{
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
for循环出局条件的i已经超出了查找范围,也就是我们横向遍历结束了
return 是结束我们的深度遍历,跳回本层开始横向遍历
组合
因为{1,2,3}中选择两个数字有几种组合
随机选取的话就是六种,但是明显重复了组合是无序的
{1,2}、{1,3}、{2、3}、{2,1}、{3,1}、{3、2}
好马不吃回头草,我们设置一个变量startindex来控制选择的时候的搜索范围
backtracking(i++) 代表了每进行一次递归,都要选择不同的数字,就像是一根树枝,一样延伸
for(i = startindex;;i++) 我们将i = startindex ,i++ 这样每次横向搜索都是向前的
本质上最核心的就是好马不吃回头草的,我们使用上面两个i++,实现
排列
对应排列来说
{1,2}、{1,3}、{2、3}、{2,1}、{3,1}、{3、2},这六个都是不一样的,好马可以吃回头草
这样我们就不需要startindex,当时也不能乱选,出现{1,1}、{2、2}我们要保证一个元素只能选择次
这样的话我们就引入了数组used,要是谁使用了这样元素就在used中标记一下,回溯的时候就标记还原
去重
我们树枝是可以选择重复数字的,但是同一个树层是不可以选择同一数字,尽管它们不是同一个
1、sort排序
2、在插入之前判断这个前面的是不是相同
3、判断是不是树层重复
题型总结
一、组合
对于组合来说{1,2} 和{2,1}都是一样的结果,所以为了避免出现一样的结过,我们使用startindex控制每次取数字的可以选用范围
void backtracking(int n, int k, int startIndex) {
if (path.size() == k) {
result.push_back(path);
return;
}
for (int i = startIndex; i <= n; i++) {
path.push_back(i); // 处理节点
backtracking(n, k, i + 1); // 递归
path.pop_back(); // 回溯,撤销处理的节点
}
}
名词解释 n:可以选择的个数 k:我们需要的个数 path 存放我们选择中的组合 result :存放我们已经选择好的组合
startindex:控制我们每次选择数字的搜索范围,避免发生重复的事情 回溯:退出一个元素,然后我们就可以继续选择注意:startindex在每次递归一层就会加一,每次横向for循环一次也会加一
流程解释 1、等我们path存放了足够的数字,就执行终止条件,跳回本层,相对的就是执行完了本层的backtraceing()
2、path.pop_back() 弹出一个数值,i++
3、继续for循环,等到本层都遍历完了,就会由于for循环结束。跳到上一层(横向遍历)
题型分析
1、可选取的数字可以重复,可选数字无重复
只要使用上面的模板,就可以
不可以重复取一个数字:进行递归的时候startindex需要加一,不可以取之前的数字
可选取的数字无重复:结果不需要去重
2、可选择的数字可以重复
深度递归的时候,startindex不需要加一
3、可选数字有重复(横向选择重复的数字会造成结果重复)
这样我们最后的结果就会出现重复,我们要去重
1、我们传入数组的时候,使用sort排序
2、for循环中,在将数字插入path之前判断这个数字与之前插入的是否相同,并且判断是树枝重复还是树层重复
只有树层重复才要舍去
4、数字与号码
1、先将号码转换成字符,将10个数字对应的字符存放到一个容器中
2、这样我们每次选择的数组都是不同的,我们要做好调整
2、分割
终止条件是
我们切割线已经到了最后一个数字后面
startindex是从0开始,当startindex >=s.size(),已经切空了
什么是回文串
使用双指针,一个指针从前往后,一个指针从后往前,如果前后指针所指向的元素是相等的,就是回文子串
题型分析
1、普通回文子串
在插入path之前切下的是不是回文串,要是不是就继续横向遍历下一个
2、复用IP地址
与普通回文子串差不多在插入path之前也要查看是否合法、不行就下一行,当时这个比较特殊一旦前面出现不合法的后面都不合法了,直接break
终止条件
我们需要切四段,没切成就会在后面加一都好,等逗号数目为三,就表示切完了,要是最后一段也合法,就放进result
3、子集
1、普通子集
子集不是收集最后结果,而是过程产生的结果也要,result就不是存放在终止条件的下面,而是上面这样过程都能收集
2、集合有重复元素’
比上面一个多了一个去重,在插入之前判断是否横向重复是的话就跳过
4、排列
1、普通全排列
不要startindex,每次搜索都是重头开始,使用一个used数组来存放标记,来记录哪些元素是否被使用
2、数组中有重复
与上面一个多了一个出重的步骤,在插入之前,要是是重的就continue