代码随想录算法训练营第24天 | 回溯算法1

回溯法也可以叫做回溯搜索法。实际上是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就回溯返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或者达不到目标,就退回一步重新选择,这种走不通就退回再走的技术称为回溯法,而满足回溯条件得某个状态的点称为回溯点。

回溯隐藏在递归下面,纯暴力搜索。

回溯法的效率

回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案。

回溯法解决的问题

  1. 组合问题:N个数里按一定规则找出k个数的集合(不强调顺序)
  2. 切割问题:一个字符按一定规则有几种切割方式
  3. 子集问题:一个N个数的集合里有多少符合条件的子集
  4. 排列问题:N个数按一定规则全排列,有几种排列方式(强调顺序)
  5. 棋盘问题:N皇后,解数独等

如何理解回溯法

回溯法解决的问题都可以抽象为树形结构。
因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度。
递归就要有终止条件,所以必然是一棵高度有限的树。

回溯法模板

  1. 回溯函数模板返回值及参数
    回溯法的参数一般是先写逻辑,然后需要什么参数就填什么参数。
void backtracking(参数)
  1. 回溯函数终止条件
    什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,就找到了满足条件得一条答案,把这个答案存放起来,并结束本层递归。
if(终止条件){
	存放结果;
	return}
  1. 回溯搜索的遍历过程
    回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成了树的深度。
    ![[Pasted image 20230208192456.png]]
for(选择:本层集合中元素(树中节点孩子的数量就是集合的大小)){
	处理节点;
	backtracking(路径, 选择列表); //递归
	回溯,撤销处理结果;
}

for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。
backtracking这里自己调用自己,实现递归。
for循环可以理解是横向遍历,backtracking就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找到的其中一个结果了。
回溯算法模板框架:

void backtracking(参数){
	if(终止条件){
		存放结果;
		return;
	}
	for(选择:本层集合中元素(树中节点孩子的数量就是集合的大小)){
		处理节点;
		backtracking(路径, 选择列表); //递归
		回溯,撤销处理结果
	}
}

77.组合

题目: 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
链接: https://leetcode.cn/problems/combinations/
思路:

  1. vector> result 存放结果的集合。
    vector path 存放符合条件的结果。
  2. 回溯法模板三部曲
    ①确定参数和返回值
    参数:int n, int k, int startIndex
    startIndex用于确定节点的先后关系,避免得到重复的结果。
    ②终止条件
    path中存放的数满足k个。
    ③遍历过程
    for循环从startIndex 开始遍历到n ,处理操作就是把当前的遍历值加入到path里面。path.size() == k开始回溯,撤销到上一个节点。
class Solution {
private:
	vector<vector<int>> result;
	vector<int> path;
	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();
		}
	}
public:
	vector<vector<int>> combine(int n, int k){
		backtracking(n, k, 1);
		return result;
	}
}

你可能感兴趣的:(代码随想录,算法,c++,开发语言)