补day27--回溯算法1

基础理论

0、关于回溯算法可以解决哪些问题?

(1)组合问题,给一个数组1234,元素总和为2的组合有多少个?

(2)切割问题,给一个数组,对数组的元素按照特定的规则切割成新的数组

(3)子集问题,给一个数组,求数组的子集

(4)排列问题,和上面的组合问题相似,但是不强调元素的顺序问题

(5)棋盘问题,N皇后或者解数独问题

1、如何理解回溯法

可以将其总结为一棵树,深度和宽度与回溯的循环有关

回溯算法实质是一个穷举法,所以效率不高,适合一些暴力搜索的题;

2、实现逻辑

void back(参数){

if(终止条件){

收集结果;   //因为结果都在叶子节点上,所以遍历到叶子节点上的时候开始收集结果

return ;

}

for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)){

处理节点;

递归函数;

回溯算法;

}

return ;

}


第一题:组合

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

方法一:直接for循环暴力求解,但是在数据量很大的时候,效率很低;

方法二:回溯法

三部曲:

(1)递归函数的返回值以及参数

vector<vector<int>> result;  //存放符合条件结果的集合

vector<int> path;  //用来存放符合条件的单一结果

void backtracking(int n,int k ,int startIndex)   //集合n中取k个数,而startIndex用于控制下一层的递归,避免重复

(2)确定终止条件

如果path这个数组的大小如果达到k,说明找到了一个子集大小为k的组合,用result二维数组将path保存起来,终止本层递归

if(path.size()==k){

result.push_back(path);

return ;

}

(3)单层搜索逻辑

横向利用startIndex作为遍历起点,fo循环,然后用path保存取到的节点i

for(int i=startIndex; i<=n; i++){

path.push_back(i);  //处理节点

backtracking(n,k,i+1);   //递归:控制树的纵向遍历,注意下一层搜索从i+1开始

path.pop_back();  //回溯,撤销处理的节点

}

整体逻辑还是根回溯算法大纲差不多,处理、递归、回溯

第二题:组合总和3

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

0、思路:观察可得,构建的树最大宽度为9,深度为k,按照上题的逻辑即可求得

1、回溯三部曲

(1)确定递归函数

还是需要全局变量,保存路径和返回结果

vector<vector<int>> result;

vector<int> path;

void backtracking(int targetSum,int k,int sum, int startIndex)

(2)确定终止条件

还是一样,path取够k长度就停止

if(path.size()==k){

if(sum==targetSum)  result.push_back(path);

return ;

}

(3)单层搜索过程:处理,递归,回溯

 for(int i=startIndex;i<=9-(k-path.size())+1;i++){

sum+=i;

path.push_back(i);//处理

backtracking(targetSum,k,sum,i+1);  //递归

sum-=i;

path.pop_back();  //回溯

}

关于为什么sum要加了再减,sum是用来寻找合适的值,所以加了然后递归进去判断,为什么要减,是因为一个叶子节点判断完,需要回去判断另一个叶子;有加就有减

注意点:for循环内,i应该根据path的长度以及k的大小来决定,同时避免重复

第三题:电话号码的字母组合

0、思路:首先,0-9分别对应不同的字母集合,所以横向是每个数字对应的字符集合

然后,深度是集合长度,比如23,k=2

先写出每个数字对应的字符组合:

const string letterMap[10] = {
        "", // 0
        "", // 1
        "abc", // 2
        "def", // 3
        "ghi", // 4
        "jkl", // 5
        "mno", // 6
        "pqrs", // 7
        "tuv", // 8
        "wxyz", // 9
    };

1、回溯三部曲

(1)确定回溯函数参数,同时定义全局变量

vector<string> result;

string s;

void backstracking(const string & digits,int index)

(2)确定终止条件

用index控制层数,如果层数到达数组长度,即停止

if(index==digits.size()){

result.push_back(s);

return;

}

(3)确定单层递归逻辑

处理、递归、回溯

int digit=digits[index]-'0';

string letters=letterMap[digit];

for(int i=0;i<letters.size();i++){

s.push_back(letters[i]);

backtracking(digits,index+1);

s.pop_back();

}

第三题需要二刷!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值