算法题复习(回溯)

目录

  • base code
  • 棋盘问题
    • 51. N 皇后
    • 37. 解数独
  • 组合问题
    • 77. 组合
      • 未剪枝优化
      • 剪枝优化
    • 216. 组合总和 III
      • 未剪枝优化
      • 剪枝优化
    • 17. 电话号码的字母组合
    • 39. 组合总和
      • 未剪枝优化
      • 剪枝优化
    • 40. 组合总和 II,挺重要的,涉及到去重了
  • 切割问题
    • 131. 分割回文串
  • 子集问题
    • 78. 子集
    • 90. 子集 II,有树层去重
    • 491. 递增子序列,也需要数层去重,使用unordered_set
  • 排列问题
    • 46. 全排列
    • 47. 全排列 II + 数层去重
  • 行程安排+高级容器的使用,现在还没吃透

base code

回溯法大致思路

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

棋盘问题

51. N 皇后

https://leetcode-cn.com/problems/n-queens/

class Solution {
private:vector<vector<string>> result;
public:bool isValid(int row, int col, vector<string>& chessboard, int n){//检查列for(int i = 0; i < row; i++){if(chessboard[i][col] == 'Q') return false;}//检查左上方有无for(int i = row - 1,j = col - 1; i >= 0 && j >= 0; i--, j--){if(chessboard[i][j] == 'Q') return false;}//检查右上方有无for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++){if(chessboard[i][j] == 'Q') return false;}return true;}//n表示棋盘边长,row表示当前遍历到第几层了void backtracking(int n, int row, vector<string>& chessboard){//当递归到棋盘最底层的时候,收集结果if(row == n){result.push_back(chessboard);return;}//单层逻辑,递归深度row控制行,每一层for循环col控制列for(int col = 0; col < n; col++){if(isValid(row,col,chessboard,n) == true){chessboard[row][col] = 'Q';    //放置皇后backtracking(n,row+1,chessboard);chessboard[row][col] = '.';}}return ;}vector<vector<string>> solveNQueens(int n) {result.clear();//注意一下棋盘的初始化方式vector<string> chessboard(n,string(n,'.'));backtracking(n,0,chessboard);return result;}
};

37. 解数独

https://leetcode-cn.com/problems/sudoku-solver/

class Solution {
public:bool isValid(int row, int col, char val,vector<vector<char>>& board){//判断同行是否重复for(int i = 0; i < 9; i++){if(board[row][i] == val) return false;}//判断同列是否重复for(int i = 0; i < 9; i++){if(board[i][col] == val) return false;}//判断9方格里是否重复int startRow = (row / 3) * 3;int startCol = (col / 3) * 3;for(int i = startRow; i < startRow + 3; i++){for(int j = startCol; j < startCol + 3; j++){if(board[i][j] == val) return false;}}//如果都没错return true;}bool backtracking(vector<vector<char>>& board){for(int i = 0; i < board.size(); i++){for(int j = 0; j < board[0].size(); j++){//如果已经填入,continueif(board[i][j] != '.')  continue;for(char k = '1'; k <= '9'; k++)    //观察(i,j)这个位置放置k是否合适{if(isValid(i,j,k,board)){board[i][j] = k;            //放置if(backtracking(board)) return true;    //如果找到一组合适的解,立刻返回board[i][j] = '.';          //回溯,撤销k}}return false;                       //9个数都试完了,都不行,返回false;}}return true;                                //遍历完没有返回false,说明找到了合适的棋盘}void solveSudoku(vector<vector<char>>& board) {backtracking(board);}
};

组合问题

77. 组合

未剪枝优化

https://leetcode-cn.com/problems/combinations/
逻辑树同一层:从左向右取数,取过的数不再重复取
n相当于树宽,k相当于树深度。
每次搜索到叶子节点,就找到了一个结果。

class Solution {
private:
vector<vector<int>> result;     //用来存放结果集合
vector<int> path;               //用来存放结果
public: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);  //下一层搜索从i+1开始path.pop_back();        //回溯}  }vector<vector<int>> combine(int n, int k) {result.clear();path.clear();backtracking(n,k,1);return result;}
};

剪枝优化

单层逻辑中
如果for循环选择的起始位置之后的元素个数已经不足,就没有必要再次搜索。
已经选择path.size(),还需要k - path.size(),所以可以这样写:

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

216. 组合总和 III

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

未剪枝优化

class Solution {
public:vector<vector<int>> result;vector<int> path;//  目标和  树深度  当前path路径和  本层起始点   void backtracking(int targetSum, int k, int sum, int startIndex){if(path.size() == k)    //到达树底部{if(sum == targetSum) result.push_back(path);return;}for(int i = startIndex; i <= 9; i++){sum += i;path.push_back(i);backtracking(targetSum,k,sum,i+1);path.pop_back();sum -= i;}}vector<vector<int>> combinationSum3(int k, int n) {result.clear();path.clear();backtracking(n,k,0,1);return result;}
};

剪枝优化

如果sum > targetSum ,那么往后遍历就没有意义了。于是在backtracking函数一开始加上:

if(sum > targetSum) return;

17. 电话号码的字母组合

https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
细节:
数字与字母的映射.与前两题不同的是,本题是求不同集合之间的组合,前两题是求同一个集合中的组合。

class Solution {
private:const string letterMap[10] = {"",     //0"",     //1"abc",  //2"def",  //3"ghi",  //4"jkl",  //5"mno",  //6"pqrs", //7"tuv",  //8"wxyz", //9
};
public:vector<string> result;string s;void backtracking(const string& digits, int index){if(index == digits.size()){result.push_back(s);return;}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();}}vector<string> letterCombinations(string digits) {s.clear();result.clear();if(digits.size() == 0) return result;backtracking(digits,0);return result;}
};

39. 组合总和

https://leetcode-cn.com/problems/combination-sum/
需要注意的点:
组合没有数量要求,元素可以无限重复选取。

未剪枝优化

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex){if(sum > target) return;if(sum == target) {result.push_back(path);return;}for(int i = startIndex; i < candidates.size(); i++){sum += candidates[i];path.push_back(candidates[i]);backtracking(candidates,target,sum,i);path.pop_back();sum -= candidates[i];}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {result.clear();path.clear();backtracking(candidates,target,0,0);return result;}
};

剪枝优化

对总集合先排序,如果本层sum + candidates[i] 已经大于target,就没有必要再向本层之后的元素遍历了。

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex){if(sum > target) return;if(sum == target) {result.push_back(path);return;}for(int i = startIndex; i < candidates.size() && candidates[i] + sum <= target; i++){sum += candidates[i];path.push_back(candidates[i]);backtracking(candidates,target,sum,i);path.pop_back();sum -= candidates[i];}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {result.clear();path.clear();sort(candidates.begin(),candidates.end());backtracking(candidates,target,0,0);return result;}
};

40. 组合总和 II,挺重要的,涉及到去重了

这一题和上一题有区别:
本题candidates中的每个数字再每个组合中只能用一次,并且candidates中有元素是重复的,并且解集中不能包含重复的组合。要在搜索的过程中就去掉重复组合。
去重:使用过的元素不能重复选取。注意组合问题的逻辑树有两个维度:深度和广度
元素在同一个组合内可以重复,但是两个组合不能相同,显然是树层广度上的去重。
同时注意,对于树层去重不需要使用辅助数组。

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex){if(sum > target) return;if(sum == target){result.push_back(path);return;}for(int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++){//去重if(i > startIndex  && candidates[i] == candidates[i - 1]) continue;sum += candidates[i];path.push_back(candidates[i]);backtracking(candidates,target,sum,i+1);path.pop_back();sum -= candidates[i];}}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {result.clear();path.clear();//首先排序sort(candidates.begin(),candidates.end());backtracking(candidates,target,0,0);return result;}
};

切割问题

切割问题类似于组合问题。

131. 分割回文串

https://leetcode-cn.com/problems/palindrome-partitioning/

class Solution {
public:vector<vector<string>> result;vector<string> path;bool isHuiWen(const string& s,int start,int end){int i = start;int j = end;while(i < j){if(s[i] != s[j]) return false;i++;j--;}return true;}void backtracking(const string& s,int startIndex){if(startIndex >= s.size()){result.push_back(path);return;}for(int i = startIndex; i < s.size(); i++){//如果是回文子串if(isHuiWen(s,startIndex,i)){//获取子串string str = s.substr(startIndex,i - startIndex + 1);path.push_back(str);backtracking(s,i+1);path.pop_back();}}}vector<vector<string>> partition(string s) {result.clear();path.clear();backtracking(s,0);return result;}
};

子集问题

子集问题也是一种组合问题。不过不同的是组合问题是在到达叶子节点时候才将path送入result。而子集问题每次进入这一层就要把path加入result,这才是子集。

78. 子集

https://leetcode-cn.com/problems/subsets/

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

90. 子集 II,有树层去重

https://leetcode-cn.com/problems/subsets-ii/
比之前加上一个树层去重

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, int startIndex){//result.push_back(path);if(startIndex > nums.size()) return;for(int i = startIndex; i < nums.size(); i++){//树层去重if(i > startIndex && nums[i-1] == nums[i]) continue;path.push_back(nums[i]);backtracking(nums,i+1);path.pop_back();}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {result.clear();path.clear();sort(nums.begin(),nums.end());backtracking(nums,0);return result;}
};

491. 递增子序列,也需要数层去重,使用unordered_set

https://leetcode-cn.com/problems/increasing-subsequences/
去重逻辑:
1、使用unordered_set用来记录本层元素是否重复,重复则continue
2、观察待插入的元素nums[i]是否是比path尾部元素大等的,否则构成不了递增序列。

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, int startIndex){//子序列长度>=2if(path.size() > 1)result.push_back(path);if(startIndex > nums.size()) return;unordered_set<int> uset;for(int i = startIndex; i < nums.size(); i++){//树层去重if(!path.empty() && nums[i] < path.back()) continue;if(uset.find(nums[i]) != uset.end()) continue;uset.insert(nums[i]);path.push_back(nums[i]);backtracking(nums,i+1);path.pop_back();}}vector<vector<int>> findSubsequences(vector<int>& nums) {result.clear();path.clear();backtracking(nums,0);return result;}
};

排列问题

46. 全排列

https://leetcode-cn.com/problems/permutations/
与组合的区别在于for循环从0开始,但是需要注意取过的元素不能再取,这里使用used数组进行记录。

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, vector<bool>& used){if(path.size() == nums.size()) {result.push_back(path);return;}for(int i = 0; i < nums.size(); i++){//用过的元素不需要使用if(used[i] == true) continue;used[i] = true;path.push_back(nums[i]);backtracking(nums,used);path.pop_back();used[i] = false;}}vector<vector<int>> permute(vector<int>& nums) {result.clear();path.clear();vector<bool> used(nums.size(),false);backtracking(nums,used);return result;}
};

47. 全排列 II + 数层去重

https://leetcode-cn.com/problems/permutations-ii/

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, vector<bool>& used){if(path.size() == nums.size()) {result.push_back(path);return;}for(int i = 0; i < nums.size(); i++){//去重,同一层nums[i-1]使用过的话直接跳过if(i > 0 && nums[i] == nums[i-1] && used[i-1] == true) continue;//用过的元素不需要使用if(used[i] == true) continue;used[i] = true;path.push_back(nums[i]);backtracking(nums,used);path.pop_back();used[i] = false;}}vector<vector<int>> permuteUnique(vector<int>& nums) {result.clear();path.clear();sort(nums.begin(),nums.end());vector<bool> used(nums.size(),false);backtracking(nums,used);return result;}
};

行程安排+高级容器的使用,现在还没吃透

https://leetcode-cn.com/problems/reconstruct-itinerary/
还得再多看看。

class Solution {
public://unordered_map<出发机场,map<到达机场,航班次数>> targets;unordered_map<string,map<string,int>> targets;//还有多少航班 tickets//由于只需要找到一个行程,所以找到直接返回,使用bool正好bool backtracking(int ticketNum,vector<string>& result){if(result.size() == ticketNum + 1)  return true;for(pair<const string,int>& target : targets[result[result.size() - 1]]){if(target.second > 0) {result.push_back(target.first);target.second--;if(backtracking(ticketNum,result)) return true;result.pop_back();target.second++;}}return false;}vector<string> findItinerary(vector<vector<string>>& tickets) {targets.clear();vector<string> result;//初始化targetsfor(const vector<string>& vec : tickets){targets[vec[0]][vec[1]]++;  //记录映射关系}result.push_back("JFK");    //起始机场backtracking(tickets.size(),result);return result;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/377318.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pfa是什么意思_PFA的完整形式是什么?

pfa是什么意思PFA&#xff1a;预测性故障分析 (PFA: Predictive Failure Analysis) PFA is an abbreviation of Predictive Failure Analysis. It is a technique of a mechanism of the computer that is used to predict impending failures of software or hardware compone…

SqlServer视图(view)

--上节回顾--1.什么是事务--2.事务的特征--原子性、一致性、隔离性、持久性--3.与事务相关的t-sql命令--开启事务&#xff1a;begin transaction--提交事务&#xff1a;commit transaction--回滚事务&#xff1a;rollback transaction ----------------视图-------------------…

Android中的广播Broadcast详解

今天来看一下Android中的广播机制&#xff0c;我们知道广播Broadcast是Android中的四大组件之一&#xff0c;可见他的重要性了&#xff0c;当然它的用途也很大的&#xff0c;比如一些系统的广播&#xff1a;电量低、开机、锁屏等一些操作都会发送一个广播&#xff0c;具体的And…

sql check约束_在SQL中使用CHECK约束

sql check约束Basically, CHECK constraint is used to LIMIT in columns for the range of values. We can use this constraint for single or multiple columns. 基本上&#xff0c; CHECK约束用于限制值范围内的列 。 我们可以将此约束用于单列或多列。 In single column,…

.NET线程池

摘要 深度探索 Microsoft .NET提供的线程池&#xff0c; 揭示什么情况下你需要用线程池以及 .NET框架下的线程池是如何实现的&#xff0c;并告诉你如何去使用线程池。 内容 介绍 .NET中的线程池 线程池中执行的函数 使用定时器 同步对象的执行 异步I/O操作 监视线程池 死锁 有关…

折线分割平面

Input输入数据的第一行是一个整数C,表示测试实例的个数&#xff0c;然后是C 行数据&#xff0c;每行包含一个整数n(0<n<10000),表示折线的数量。Output对于每个测试实例&#xff0c;请输出平面的最大分割数&#xff0c;每个实例的输出占一行。Sample Input2 1 2Sample Ou…

《c++特性》

目录多态构造函数和析构函数存在多态吗&#xff1f;虚函数表虚析构函数纯虚函数和抽象类运行时多态和编译时多态的区别继承设计实例指针对象和普通对象的区别正确初始化派生类方式继承和赋值的兼容规则protected 和 private 继承基类与派生类的指针强制转换如何用C实现C的三大特…

Scala中的while循环

在Scala中的while循环 (while loop in Scala) while loop in Scala is used to run a block of code multiple numbers of time. The number of executions is defined by an entry condition. If this condition is TRUE the code will run otherwise it will not run. Scala中…

Linux操作系统启动过程

在做开发的过程中&#xff0c;突然发现&#xff0c;要对系统做一些有意义的改变&#xff0c;必须要对操作系统的启动过程有一定的了解&#xff0c;不然就是修改你都不知道从哪里下手啊&#xff0c;然后就是找来资料看&#xff0c;去网上看别人的博客&#xff0c;有了前一周一些…

方法命名的区别

GetDecimalFromString ExtractDecimal 这2个方法名那个比较好呢。上边的明显的是中式英语&#xff0c;单词拼凑而成的。下边的更加流畅一些。方法名称取名还是很有要求的。要通俗易懂还要符合文法。从上边的可以扩展出什么想法呢。 ExtractDecimalExtractDoubleExtractInt16Ext…

工作排序问题

Problem statement: 问题陈述&#xff1a; Given an array of jobs where every job has a deadline and a profit. Profit can be earned only if the job is finished before the deadline. It is also given that every job takes a single unit of time, so the minimum p…

牛客网与leetcode刷题(高频题中简单or中等的)

目录1、反转链表2、排序3、先序中序后序遍历4、最小的k个数5、子数组的最大累加和6、 用两个栈实现队列7、142. 环形链表 II8、20. 有效的括号9、最长公共子串(动态规划),磕磕绊绊10、二叉树之字形层序遍历11、重建二叉树12、LRU缓存13、合并两个有序链表15、大数加法16、一个二…

AMUL的完整形式是什么?

AMUL&#xff1a;阿南德牛奶联盟有限公司 (AMUL: Anand Milk Union Limited) AMUL is an abbreviation of Anand Milk Union Limited. It is an Indian milk product cooperative dairy organization that is based in the small town of Anand in the state of Gujarat. AMUL …

mochiweb 源码阅读(十一)

大家好&#xff0c;今天周六&#xff0c;继续接着上一篇&#xff0c;跟大家分享mochiweb源码。上一篇&#xff0c;最后我们看到了mochiweb_socket_server:listen/3函数&#xff1a; listen(Port, Opts, State#mochiweb_socket_server{sslSsl, ssl_optsSslOpts}) ->case moch…

Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转)

转载请注明出处&#xff1a;http://blog.csdn.net/guolin_blog/article/details/9255575 最 近项目中需要用到ListView下拉刷新的功能&#xff0c;一开始想图省事&#xff0c;在网上直接找一个现成的&#xff0c;可是尝试了网上多个版本的下拉刷新之后发现效果都不怎么理 想。有…

Python中的append()和extend()

列出append()方法 (List append() method) append() method is used to insert an element or a list object to the list and length of the List increased by the 1. append()方法用于将元素或列表对象插入列表&#xff0c;并且列表长度增加1。 Syntax: 句法&#xff1a; …

红黑树的实现

目录1、红黑树原理1、红黑树性质2、变换规则&#xff08;从插入结点的角度来讲&#xff09;1.变色2.左旋3.右旋3、删除结点需要注意的地方2、代码1、定义结点以及构造函数2、定义红黑树类以及声明它的方法3、左旋4、右旋5、插入操作6、修正操作7、删除操作3、参考链接1、红黑树…

118 - ZOJ Monthly, July 2012

http://acm.zju.edu.cn/onlinejudge/showContestProblems.do?contestId339 都是赛后做的。。。弱爆了 A题是找由2和5组成的数字的个数 直接打个表就行了 只是比赛的时候不知道怎么打表啊。。 View Code #include<cstdio> #include<cstring> #include<algorith…

edp1.2和edp1.4_EDP​​的完整形式是什么?

edp1.2和edp1.4EDP​​&#xff1a;电子数据处理 (EDP: Electronic Data Processing) EDP is an abbreviation of Electronic Data Processing. It alludes to the functioning of operations of commercial data, documents processing of storing, with the use of a compute…

高效读书心得

1.尽量阅读中文版 虽然有人英文很强&#xff0c;有的翻译很差&#xff0c;但AnyWay 中文阅读与理解的时间&#xff0c;略读与快速定位感兴趣内容的速度还是要快一些。 2.即时批注、总结笔记与交流 虽然爱书&#xff0c;但发现最有效的读书方式还是不断的制造脂批本&…