回溯算法(DFS)题目

news/2024/11/24 8:06:01/

文章目录

      • 1.lc77 组合
      • 2.lc216 组合总和 III
      • 3.lc17 电话号码的字母组合
      • 4.lc39 组合总和
      • 5.lc40 组合总和 II
      • 6.lc131 分割回文串
      • 7.lc3 复原 IP 地址
      • 8.lc78 子集
      • 9.lc90 子集 II
      • 10.lc491 递增子序列
      • 11.lc46 全排列
      • 12.lc47 全排列 II
      • 13.lc51 N 皇后
      • 14.lc112 路径总和
      • 15.lc113 路径总和 II
      • 16.lc22 括号生成
      • 17.lc200 岛屿数量
      • 18.lc79 单词搜索

1.lc77 组合

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

你可以按 任何顺序 返回答案。

https://leetcode.cn/problems/combinations/

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

//组合问题
//用过的不能再用,通过startindex实现
//每个元素只能取一次,通过dfs(satrtindex+1)实现class Solution {
private:vector<vector<int>> res;vector<int> path;void dfs(int n,int k,int startindex){if(path.size()==k) {res.push_back(path);return;}for(int i = startindex;i<=n;i++){path.push_back(i);dfs(n,k,i+1);path.pop_back();}}public:vector<vector<int>> combine(int n, int k) {dfs(n,k,1);return res;}
};

2.lc216 组合总和 III

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。

https://leetcode.cn/problems/combination-sum-iii/

//组合问题
//要用startindex+1确保每个数字只用一次
//通过一维的path和二维的res保存中间结果和最后结果
//判断中止的条件时path中元素的个数是否达到k个
//通过判断path中元素的sum值来决定是否将path传到res
//dfs中需要传入k,n,startindex//疑问:需不需要一个变量记录path中已有变量的sumclass Solution {
private:vector<vector<int>> res;vector<int> path;void dfs(int k,int n,int startindex){if(path.size()==k) //决定树的深度{int sum = 0;for(int i = 0;i<k;i++){sum+=path[i];}if(sum==n) res.push_back(path);return;}for(int i = startindex;i<=9;i++)  //决定树的宽度{path.push_back(i);dfs(k,n,i+1);path.pop_back();}}public:vector<vector<int>> combinationSum3(int k, int n) {dfs(k,n,1);return res;}
};

3.lc17 电话号码的字母组合

在这里插入图片描述

//首先要解决数字和对应字母之间的映射关系:定义一个string类型的数组来实现
//树的宽度为该层数字对应的字符串的长度,高度为digits的长度。
//要传入dfs的是digits,phone_map,index
//判断中止的条件是index为digits的长度class Solution {
private:vector<string> res;string path;const string phone_map[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};void dfs(string digits,int index){if(index==digits.size()){res.push_back(path);return;}for(int i = 0;i<phone_map[digits[index]-'0'].size();i++){path.push_back(phone_map[digits[index]-'0'][i]);dfs(digits,index+1);path.pop_back();}}public:vector<string> letterCombinations(string digits) {if(digits.size()==0) return res;dfs(digits,0);return res;}
};

4.lc39 组合总和

在这里插入图片描述

//依旧是组合问题,源数组无重复,但是可以重复选取
//和之前的"组合总和 III"主要区别在于源数组中的数能否重复的选取
//这个区别的话通过dfs中startindex不加1来实现
//这样的话中止条件需要通过path数组中已有元素的和是否超过target来判断
//为了实现实现上面的判断就需要在dfs中传入参数时,传入一个sum表示当前path的元素的和
//树的宽度为当前startindex下可以选取的元素数量class Solution {
private:vector<vector<int>> res;vector<int> path;void dfs(vector<int>& candidates,int target, int sum, int starindex){   if(sum>target) return;if(sum==target) {res.push_back(path);return;}for(int i = starindex;i<candidates.size();i++){sum+=candidates[i];path.push_back(candidates[i]);dfs(candidates,target,sum,i);sum-=candidates[i];path.pop_back();}}public:vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
//        dfs(candidates,target,sum,starindex);dfs(candidates,target,0,0);return res;}
};

5.lc40 组合总和 II

在这里插入图片描述

//这个跟上一个lc39 组合总和很像,区别在于本题源数组可以有重复,而对于数组中每个元素只可以取一次
//为了不重复还是需要用到startindex,到这里发现组合问题都需要startindex哈,到后面的排列问题就不需要了
//由于源数组中有重复元素,即使用了startindex依旧需要去重,这时如果考虑最后去重会花很多时间,可能导致超时吧
//于是乎,可以先对源数组排一下序,然后用一个used记录元素是否用过
//如果当前元素和前一个元素相等,而且used前一个元素时true代表什么呢?代表这俩在一个树枝上
//如果当前元素和前一个元素相等,而前一个元素used为false代表什么呢?代表这俩在同一层上
// 1 1 1 2 4 target=5 同一层上的情况直接跳过,因为它能贡献的情况,前面那个和他相同的值也能提供,并且提供更多
//树的宽度是startindex右边的数的大小,高度通过不限制但是sum超过target剪枝
//写代码时注意先排序,另外去掉同层的情况要写在循环中,

class Solution {vector<vector<int>> res;vector<int> path;void dfs(vector<int>& candidates,int target,vector<bool>& used,int sum, int startindex){if(sum>target) return;if(sum==target) {res.push_back(path);return;}for(int i = startindex;i<candidates.size();i++){if(i>0&&candidates[i-1]==candidates[i]&&used[i-1]==false)  continue;sum+=candidates[i];path.push_back(candidates[i]);used[i] = true;dfs(candidates,target,used,sum,i+1);sum-=candidates[i];path.pop_back();used[i] = false;}}
public:vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {sort(candidates.begin(),candidates.end());vector<bool> used(candidates.size(), false);
//        dfs(candidates,target,sum,startindex)dfs(candidates,target,used,0,0);return res;}
};

6.lc131 分割回文串

在这里插入图片描述
//切割问题
//可切割的位置就是每个字符之后
//树的宽为startindex右边数量,深度不定
//终止条件根据startindex是否到最后一个元素后面确定
//一个很大的问题时如何表示分割线

//注:substr第一个参数是起始位置,第二个参数是长度

class Solution {
private:vector<vector<string>> res;vector<string> path;bool ishui(string s,int start,int end){bool flag = true;for(int i = start,j = end;i<j;i++,j--){if(s[i]!=s[j]) flag = false;}return flag;}void dfs(string s,int startindex){if(startindex==s.size()) {res.push_back(path);return;}for(int i = startindex;i<s.size();i++){if(ishui(s,startindex,i)){string str = s.substr(startindex,i-startindex+1);path.push_back(str);}else continue;dfs(s,i+1);path.pop_back();}}
public:vector<vector<string>> partition(string s) {//       dfs(s,startindex)dfs(s,0);return res;}
};

7.lc3 复原 IP 地址

在这里插入图片描述

//复杂一点的分割问题
//在分割问题的基础上,区别在于要加dian
//对于加dian,要求每一段范围为0-255,且不含前导0
//要确保加三个点分成四段,还是通过startindex当作点,增加一个变量记录当前的点数
//树的宽度为startindex右边的数量,深度不定
//中止条件:indexnum等于3

class Solution {
private:vector<string> res;void dfs(string s, int startindex,int indexnum){if(indexnum==3){if(isValid(s,startindex,s.size()-1)) {res.push_back(s);}return ;}for(int i = startindex;i<s.size()-1;i++){if(isValid(s,startindex,i)){s.insert(s.begin()+i+1,'.');indexnum++;dfs(s,i+2,indexnum);indexnum--;s.erase(s.begin()+i+1);}else break;}}bool isValid(string s,int start,int end){if(start>end) return false;if (s[start] == '0' && start != end) { // 0开头的数字不合法return false;}int num = 0;for(int i=start;i<=end;i++){if(s[i]-'0'>9 || s[i]-'0'< 0) return false;num= num*10 + (s[i]-'0');if(num>255) return false;}return true;}public:vector<string> restoreIpAddresses(string s) {
//        dfs(s,startindex,indexsum);if (s.size() < 4 || s.size() > 12) return res;dfs(s,0,0);return res;}
};

8.lc78 子集

在这里插入图片描述
//子集问题
//在组合问题基础上,需要将每个节点的结果保存下来
//回顾一下组合问题用startindex确保不重复,dfs的时候加1

class Solution {
private:vector<vector<int>> res;vector<int> path;void dfs(vector<int> nums,int startindex){res.push_back(path);if(startindex==nums.size()){return;}for(int i = startindex;i<nums.size();i++){path.push_back(nums[i]);dfs(nums,i+1);path.pop_back();}}
public:vector<vector<int>> subsets(vector<int>& nums) {//       dfs(nums,startindex);dfs(nums,0);return res;}
};

9.lc90 子集 II

在这里插入图片描述
//还是子集问题,区别在于这次源数组中可以有重复元素,需要加入used数组,排序方式对结果去重

class Solution {
public:vector<vector<int>> res;vector<int> path;void dfs(vector<int>& nums,vector<bool>& used,int startindex){res.push_back(path);if(startindex==nums.size()) return;for(int i = startindex;i<nums.size();i++){if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false) continue;path.push_back(nums[i]);used[i]=true;dfs(nums,used,i+1);path.pop_back();used[i] = false;}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {vector<bool> used(nums.size(),false);sort(nums.begin(),nums.end());//       dfs(nums,used,startindex);dfs(nums,used,0);return res;}
};

10.lc491 递增子序列

在这里插入图片描述
//和前面的子集问题十分类似
//要注意的是不能对数组进行排序,那么怎么实现去重呢,方法就是用一个哈希表存同一层的树结点
//当判断是否要往一个path中加数据时,要看当前值和path中最后一个数的大小的比较以及看同层是否出现过该数值

class Solution {
private:vector<vector<int>> res;vector<int> path;void dfs(vector<int> nums,int startindex){if(path.size()>1) res.push_back(path);if(startindex==nums.size()) return ;unordered_set<int> u; //注意不要定义在循环中for(int i = startindex;i<nums.size();i++){if((!path.empty()&&nums[i]<path.back())||u.find(nums[i])!=u.end()) continue;else{u.insert(nums[i]);path.push_back(nums[i]);dfs(nums,i+1);path.pop_back();}}}public:vector<vector<int>> findSubsequences(vector<int>& nums) {
//        dfs(nums,startindex);dfs(nums,0);return res;}
};

11.lc46 全排列

在这里插入图片描述
//排列问题
//不用startindex了
//那么如何去重呢,就需要用到used数组了我想,之前组合问题中对于源数组中有重复元素时用到used数组进行去重

class Solution {
private:vector<vector<int>> res;vector<int> path;void dfs(vector<int> nums,vector<bool> used,int u){if(u==nums.size()) {res.push_back(path);return ;}for(int i = 0;i<nums.size();i++){if(used[i]==false){path.push_back(nums[i]);used[i] = true;dfs(nums,used,u+1);used[i] = false;path.pop_back();}}}public:vector<vector<int>> permute(vector<int>& nums) {vector<bool> used(nums.size(),false);
//        dfs(nums,used,u);dfs(nums,used,0);return res;}
};

12.lc47 全排列 II

在这里插入图片描述
//全排列问题进阶,与上题不同在于源数组中有重复的元素
//如果还和上次那样的代码会出现什么问题呢,会出现重复

//怎么解决呢?初步想法是和组合问题中这种情况类似,先排序然后针对同层的元素用used数组判断是否要跳过

class Solution {
private:vector<vector<int>> res;vector<int> path;void dfs(vector<int> nums,vector<bool> used,int u){if(u==nums.size()) {res.push_back(path);return;}for(int i=0;i<nums.size();i++){if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false) continue;if(used[i]==false) {path.push_back(nums[i]);used[i] = true;dfs(nums,used,u+1);used[i] = false;path.pop_back();}}}
public:vector<vector<int>> permuteUnique(vector<int>& nums) {vector<bool> used(nums.size(),false);sort(nums.begin(),nums.end());
//        dfs(nums,used,u);dfs(nums,used,0);return res;}
};

13.lc51 N 皇后

在这里插入图片描述
//主要是dg和udg数组的运用,dg和udg代表正负斜线的截距
//dg[u+i]即y = -x + b
//udg[u-i+n]即y = x+b 之所以加n是因为不然会出现负数的下标,这样都加n也可以一一对应上
//--------->
//|
//|
//|
//>

class Solution {
private:bool col[20],dg[20],udg[20];vector<vector<string>> res;vector<string> path;void dfs(int u,int n){if(u==n) {res.push_back(path);return;}for(int i = 0;i< n;i++){if(!col[i]&&!dg[u+i]&&!udg[u-i+n]){string s;for(int j = 0;j<n;j++){if(j==i) s+="Q";else s+=".";}path.push_back(s);col[i]=true;dg[u+i] = true;udg[u-i+n] =true;dfs(u+1,n);path.pop_back();col[i] = false;dg[u+i] = false;udg[u-i+n] = false;}else{continue;}}}public:vector<vector<string>> solveNQueens(int n) {dfs(0,n);return res;}
};

14.lc112 路径总和

在这里插入图片描述

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {int sum = 0;bool flag = false;void dfs(TreeNode* root, int targetSum){sum += root->val;if(root->left!=nullptr) hasPathSum(root->left,targetSum);if(root->right!=nullptr) hasPathSum(root->right,targetSum);if(root->left==nullptr&&root->right==nullptr) {if(sum==targetSum) flag = true;}sum-=root->val;}
public:bool hasPathSum(TreeNode* root, int targetSum) {if(root==nullptr) return false;dfs(root,targetSum);return flag;      }
};

15.lc113 路径总和 II

在这里插入图片描述

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*///和112.路径总和的区别在于要存一下路径//应该也不难,试一试
class Solution {
private:vector<vector<int>> res;vector<int> path;int sum = 0;void dfs(TreeNode* root, int targetSum){sum += root->val;path.push_back(root->val);if(root->left!=nullptr) dfs(root->left,targetSum);if(root->right!=nullptr) dfs(root->right,targetSum);if(root->left==nullptr&&root->right==nullptr) {if(sum==targetSum) {res.push_back(path);}}sum-=root->val;path.pop_back();}
public:vector<vector<int>> pathSum(TreeNode* root, int targetSum) {if(root==nullptr) return res;dfs(root,targetSum);return res;}
};

16.lc22 括号生成

在这里插入图片描述
//dfs
//构建一个树形结构,树的宽度为2^n,树的深度为n*2
//剪枝:在生成结果过程中对于left>n(或者right>n的)或者right>left的要进行return


class Solution {
private:vector<string> res;int u = 0;string path;void dfs(int n,int u,int left,int right){if(left>n||right>left) return;if(u==2*n) {res.push_back(path);return;}path.push_back('(');dfs(n,u+1,left+1,right);path.pop_back();path.push_back(')');dfs(n,u+1,left,right+1);path.pop_back();}
public:vector<string> generateParenthesis(int n) {//dfs(n,u,right,left)dfs(n,0,0,0);return res;}
};

17.lc200 岛屿数量

在这里插入图片描述

//用一个bool数组记录是否遍历过,遍历整个数组//类似对连着的染一下色,遍历的时候没到一个岛屿且没染色的话说明是一个独立的岛屿,最后得到结果class Solution {
public:bool used[310][310];void dfs(vector<vector<char>>& grid,int i,int j){int m = grid.size();int n = grid[0].size();used[i][j] = true;int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};for(int k=0;k<4;k++){int x = i+dx[k], y = j+dy[k];if(x>=0&&x<m&&y>=0&&y<n&&grid[x][y]=='1'&&used[x][y]==false){dfs(grid,x,y);}}return;}int numIslands(vector<vector<char>>& grid) {int res = 0;int m = grid.size();int n = grid[0].size();for(int i = 0;i<m;i++){for(int j = 0;j<n;j++){if(grid[i][j]=='1'&&used[i][j]==false) {res++;dfs(grid,i,j);}else continue;}}return res;}
};

18.lc79 单词搜索

在这里插入图片描述


//需要从每个位置开始遍历,该位置的字符不等与word[0]跳过
//需要用一个bool数组记录每个位置是否用过
//需要判断和word是否相同//用一个u记录当前层数,看与对应的word的索引下的字符是否相同class Solution {
public:bool flag = false;void dfs(vector<vector<char>>& board,string word,int u,bool used[7][7],int i,int j){if(u==word.size()) flag=true;int m = board.size();int n = board[0].size();int dx[4] = {0,0,1,-1}, dy[4] = {1,-1,0,0,};for(int k = 0;k<4;k++){int x =i + dx[k],y = j+dy[k];if(x>=0&&x<m&&y>=0&&y<n){if(board[x][y]==word[u]&&used[x][y]==false){used[x][y] = true;dfs(board,word,u+1,used,x,y);used[x][y] = false;}}}}bool exist(vector<vector<char>>& board, string word) {int m = board.size();int n = board[0].size();for(int i = 0;i<m;i++){for(int j =0;j<n;j++){if(board[i][j]==word[0]){bool used[7][7] = {false};used[i][j] = true;dfs(board,word,1,used,i,j); // dfs(board,word,u);}}}return flag;}
};

http://www.ppmy.cn/news/769809.html

相关文章

lc刷题总结(回溯算法第一次)

lc77. 组合 回溯模板 void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择&#xff1a;本层集合中元素&#xff08;树中节点孩子的数量就是集合的大小&#xff09;) {处理节点;backtracking(路径&#xff0c;选择列表); // 递归回溯&#xff0c;撤销处理结果…

LC39-组合总和----LC40-组合总和 II

文章目录 [39. 组合总和](https://leetcode-cn.com/problems/combination-sum/)[40. 组合总和 II](https://leetcode-cn.com/problems/combination-sum-ii/) 39. 组合总和 难度中等1141 给定一个无重复元素的数组 candidates 和一个目标数 target &#xff0c;找出 candidate…

Paddle OCR 安装使用教程

文章目录 一、简介二、使用教程三、模型调用四、效果展示 一、简介 PaddleOCR是飞浆开源文字识别模型&#xff0c;最新开源的超轻量PP-OCRv3模型大小仅为16.2M。同时支持中英文识别&#xff1b;支持倾斜、竖排等多种方向文字识别&#xff1b;支持GPU、CPU预测&#xff0c;并且…

LC_ALL的含义

在看nginx的编译脚本时&#xff0c;其开始就 LC_ALLC export LC_ALL LC_ALL表示什么呢? LC_ALLC是为了去除所有的本地化的设置。让命令能正确执行 linux中通过locale来设置程序运行的不同语言环境 ,locale由ANSI C 提供支持。locale的命名规则为<语言>_<地区>.<…

【4.17】lc回溯问题:40 90

lc回溯问题 40.组合总和||分析代码 90.子集||代码 总结 40.组合总和|| 链接直达 分析 回溯问题 回溯三步法&#xff1a; 确定回溯函数back的参数回溯的终止条件把握回溯的搜索过程 具体题目分析&#xff1a; 注意解集不能有重复的组合&#xff0c;那么如果我们在candidate…

LC40. 组合总和 II

设置的begin其实就是放元素的位置&#xff01;&#xff01;&#xff01;没声明candidates数组不重复&#xff0c;所以去重时参考全排列二&#xff0c;对于数组里的重复元素需要进行去重.相比于组数总和一这题需要两次去重&#xff0c;第一次是深度优先遍历里同一支不可以重复使…

初学者一步步学习python 学习提纲

当学习Python时&#xff0c;可以按照以下提纲逐步学习&#xff1a; 入门基础 了解Python的历史和应用领域安装Python解释器和开发环境&#xff08;如Anaconda、IDLE等&#xff09;学习使用Python的交互式解释器或集成开发环境&#xff08;IDE&#xff09;进行简单的代码编写和…

密码学(二月最佳)

《主干目录》 第一代加密 隐藏 第二代加密 移位 替代 同音替代 第三代加密 维吉尼亚加密 第四代加密 Enigma(恩尼格玛机) 第五代加密 DES 第六代加密 RSA 第七代加密 量子加密 密码学数千年的发展史&#xff0c;加密与解密不断博弈。 加密解密算法层出不穷&#xff0c;因此抓住…