回溯总结
解题思路:
1.DFS 和回溯算法区别
DFS 是一个劲的往某一个方向搜索,而回溯算法建立在 DFS 基础之上的,但不同的是在搜索过程中,达到结束条件后,恢复状态,回溯上一层,再次搜索。因此回溯算法与 DFS 的区别就是有无状态重置
2.何时使用回溯算法
当问题需要 "回头",以此来查找出所有的解的时候,使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止
3.怎么样写回溯算法(从上而下,※代表难点,根据题目而变化)
①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关),与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择
括号生成
22. Generate Parentheses(回溯)
子集、组合 子集、子集 II、组合、组合总和、组合总和 II
class Solution { public: vector<vector<int>> res; void backtrack(vector<int>& candidates, vector<int>& path, int start, int sum, int target) { if(target==sum) { res.push_back(path); } for (int i = start; i < candidates.size(); ++i ) { if(sum > target) { continue; } path.push_back(candidates[i]); backtrack(candidates,path,i, sum+candidates[i],target); path.pop_back(); } } vector<vector<int>> combinationSum(vector<int>& candidates, int target) { vector<int> path; backtrack(candidates,path,0,0,target); return res; } };
class Solution { public: vector<vector<int>> res; void backtrack(vector<int>& candidates, vector<int>& path, int start, int sum, int target) { if(target == sum) { res.push_back(path); } for(int i = start; i < candidates.size(); ++i) { if(target < sum) { continue; } if (i>start && candidates[i-1]==candidates[i]) { continue; } path.push_back(candidates[i]); backtrack(candidates,path,i+1,sum+candidates[i],target); path.pop_back(); } } vector<vector<int>> combinationSum2(vector<int>& candidates, int target) { vector<int> path; sort(candidates.begin(),candidates.end()); backtrack(candidates,path,0,0,target); return res; } };
class Solution { public: vector<vector<int>> res; void backtrack(vector<int>& path,int k ,int n,int sum, int start) { if (path.size() == k && sum == n) { res.push_back(path); return; } for(int i = start; i <= 9 ; ++i) { if(sum > n) { continue; } path.push_back(i); backtrack(path,k,n,sum+i,i+1); path.pop_back(); } } vector<vector<int>> combinationSum3(int k, int n) { vector<int> path; backtrack(path,k,n,0,1); return res; } };
class Solution { public: vector<vector<int>> res; void dfs(vector<int>& path, int n, int k, int level) { if (path.size() == k) { res.emplace_back(path); return; } for (int i = level; i <= n; i++) { path.emplace_back(i); dfs(path,n,k,i+1); path.pop_back(); } } vector<vector<int>> combine(int n, int k) { vector<int> path; dfs(path,n,k,1); return res; } };
class Solution { public: vector<vector<int>> res; void backtrack(vector<int>& nums, vector<int>& path, int start) { res.push_back(path); for(int i = start; i < nums.size(); ++i) { path.push_back(nums[i]); backtrack(nums, path, i+1); path.pop_back(); } } vector<vector<int>> subsets(vector<int>& nums) { vector<int> path = vector<int>(); backtrack(nums,path,0); return res; } };
class Solution { public: vector<vector<int>> res; void backtrack(vector<int>& nums, vector<int>&path, int start) { res.push_back(path); for(int i = start; i < nums.size(); ++i) { if(i > start && nums[i]== nums[i-1]) { continue; } path.push_back(nums[i]); backtrack(nums,path,i+1); path.pop_back(); } } vector<vector<int>> subsetsWithDup(vector<int>& nums) { vector<int> path = vector<int>(); sort(nums.begin(),nums.end()); backtrack(nums,path,0); return res; } };
全排列 全排列、全排列 II、字符串的全排列、字母大小写全排列
再次总结:“排列”类型问题和“子集、组合”问题不同在于:“排列”问题使用used数组来标识选择列表,而“子集、组合”问题则使用start参数。另外还需注意两种问题的判重剪枝!
class Solution { public: vector<vector<int>> res; void backtrack(vector<int>& nums, vector<int>& path, vector<bool>& used) { if (nums.size() == path.size()) { res.push_back(path); return; } for(int i = 0;i < nums.size(); ++i) { if(!used[i]) { path.push_back(nums[i]); used[i] = true; backtrack(nums,path,used); path.pop_back(); used[i] = false; } } } vector<vector<int>> permute(vector<int>& nums) { vector<int> path; vector<bool> used(nums.size(),false); backtrack(nums,path, used); return res; } };
47. Permutations II (全排列有重复的元素)
class Solution { public: vector<vector<int>> res; void backtrack(vector<int>& nums,vector<int>& path, vector<bool>& used) { if(nums.size() == path.size()) { res.push_back(path); return; } for(int i = 0; i < nums.size(); ++i) { if(!used[i]) { if(i >0 && nums[i]==nums[i-1]&&used[i-1]) { continue; } path.push_back(nums[i]); used[i] = true; backtrack(nums,path,used); path.pop_back(); used[i] = false; } } } vector<vector<int>> permuteUnique(vector<int>& nums) { vector<int> path; sort(nums.begin(),nums.end()); vector<bool> used(nums.size(),false); backtrack(nums,path,used); return res; } };
131. Palindrome Partitioning(回文子串划分 深度优先)
搜索 解数独、单词搜索、N皇后、分割回文串、二进制手表
class Solution { public: vector<vector<string>> res; void backtrack(vector<string>& board,int level,int n) { if(level == n) { res.push_back(board); } for(int i = 0; i < n; ++i) { if (valid(board, level, i,n)) { board[level][i] = 'Q'; backtrack(board,level+1,n); board[level][i] = '.'; } } } bool valid(vector<string>& board, int level, int index,int n) { // 检查列 for (int i = level-1; i >= 0; --i) { if (board[i][index] == 'Q') { return false; } } // 检查45度角 for (int li = level -1 , ii = index -1 ;ii >= 0 && li >= 0;--li,--ii) { if (board[li][ii]=='Q') { return false; } } // 检查135度角 for (int li = level - 1, ii = index + 1 ; li >=0 && ii < n;--li,++ii) { if (board[li][ii] == 'Q') { return false; } } return true; } vector<vector<string>> solveNQueens(int n) { vector<string> board = vector<string>(n,string(n,'.')); backtrack(board,0,n); return res; } };
class Solution { public: bool backtrack(vector<vector<char>>& board) { for(int level = 0; level < board.size(); ++level) { for (int index = 0; index < board[0].size();++index) { if (board[level][index] != '.') continue; for (char val = '1';val <='9';val++) { if(valid(board,level,index,val)) { board[level][index] = val; if(backtrack(board)) return true; board[level][index] = '.'; } } return false; } } return true; } bool valid(vector<vector<char>>& board, int level, int index, char val) { // 检查列 for(int i = 0; i < 9 ;++i) { if (board[i][index] == val ) { return false; } } // 检查行 for (int i = 0;i < 9; ++i) { if (board[level][i] == val) { return false; } } // 检查小方格 int start_level = (level /3) *3; int start_index = (index /3) *3; for(int i = start_level; i < start_level+3;++i ) { for(int j = start_index; j <start_index+3;++j) { if (board[i][j] == val) { return false; } } } return true; } void solveSudoku(vector<vector<char>>& board) { backtrack(board); } };
作者:show-me-the-code-2
链接:https://leetcode-cn.com/problems/subsets/solution/c-zong-jie-liao-hui-su-wen-ti-lei-xing-dai-ni-gao-/