回溯算法解题套路框架 LeetCode 46 全排列1 ; LeetCode 47 全排列 2 ;LeetCode 51 N皇后 ;
搬运 博主 lambdadong 的博客: labuladong 的算法小抄
回溯算法的框架:
1 result = [] 2 def backtrack(路径, 选择列表): 3 if 满足结束条件: 4 result.add(路径) 5 return 6 7 # 做选择 8 将 选择i 从选择列表中移除/标记 选择i 已经选择过 9 路径.add(选择i) 10 backtrack(路径, 选择列表) 11 # 撤销选择 12 路径.remove(选择1) 13 将该选择i再加回选择列表
一、全排列问题
1.1 LeetCode 46 全排列1 求没有重复元素的数组的全排列
直接套用框架:
1 class Solution { 2 private: 3 vector<int> path; 4 vector<vector<int>> res; 5 public: 6 vector<vector<int>> permuteUnique(vector<int>& nums) { 7 //sort(nums.begin(),nums.end()); 8 int len=nums.size(); 9 vector<bool> visited(len,false); 10 backtrack(nums,visited,0); 11 return res; 12 } 13 // 路径:记录在 path 中 14 // 选择列表:visited[i] 为true 的nums[i] 15 // 结束条件:depth == nums.size() 走到"决策树"的叶节点 16 void backtrack(vector<int>& nums,vector<bool>& visited ,int depth) { 17 int len=nums.size(); 18 if ( depth == nums.size()) { 19 res.push_back(path); 20 return; 21 } 22 for (int i=0;i<len;i++) { 23 if (visited[i]) continue; 24 //if (i>0 && nums[i]==nums[i-1] && visited[i-1]) continue; 25 visited[i] = true; 26 path.push_back(nums[i]); 27 backtrack(nums,visited,depth+1); 28 path.pop_back(); 29 visited[i] = false; 30 } 31 } 32 };
1.2 LeetCode 47 全排列2 求有重复元素的数组的全排列
在平时项目中遇到这种需求,可以直接使用 c++ STL 的 next_permutation 函数生成所有的排列,
得到的结果就已经是去重过的了,且有很好的效率。 注意使用 next_permutation 前 ,需要 先将数组
排序 。代码如下:
#include<algrithm> #include<vector> using namespace std; vector<vector<int>> permuteUnique(vector<int>& nums) { sort(nums.begin(), nums.end()); vector<vector<int>>result; result.push_back(nums); while (next_permutation(nums.begin(), nums.end())) { result.push_back(nums); } return result; }
对比 1.1 没有重复的元素,使用1.1 中的代码得到的全排列中会包含重复的排列,可以在得到所有的排列之后,
使用set<vector<int>> 辅助 去重就可。
void remove_repeated_vec(vector<vector<int>> &res) { set<vector<int>> unique_set; vector<vector<int>>::iterator ite; vector<vector<int>> tmp_res; for(ite = res.begin();ite != res.end();++ite) { if(unique_set.find(*ite) == unique_set.end()) { unique_set.insert(*ite); tmp_res.push_back(*ite); } } res = tmp_res; return; }
或者直接把 1.1 中 line 7 和 line 24 两处的注释取消,
二. N皇后
1 class Solution { 2 public: 3 vector<vector<string>> res; 4 5 /* 输入棋盘边长 n,返回所有合法的放置 */ 6 vector<vector<string>> solveNQueens(int n) 7 { 8 // '.' 表示空,'Q' 表示皇后,初始化空棋盘。 9 vector<string> board(n, string(n, '.')); 10 backtrack(board, 0); 11 return res; 12 } 13 14 // 路径:board 中小于 row 的那些行都已经成功放置了皇后 15 // 选择列表:第 row 行的所有列都是放置皇后的选择 16 // 结束条件:row 超过 board 的最后一行 17 void backtrack(vector<string>& board, int row) 18 { 19 // 触发结束条件 20 if (row == board.size()) { 21 res.push_back(board); 22 return; 23 } 24 25 int n = board[row].size(); 26 for (int col = 0; col < n; col++) { 27 // 排除不合法选择 28 if (!isValid(board, row, col)) 29 continue; 30 // 做选择 31 board[row][col] = 'Q'; 32 // 进入下一行决策 33 backtrack(board, row + 1); 34 // 撤销选择 35 board[row][col] = '.'; 36 } 37 } 38 39 /* 是否可以在 board[row][col] 放置皇后? */ 40 bool isValid(vector<string>& board, int row, int col) 41 { 42 int n = board.size(); 43 // 检查列是否有皇后互相冲突 44 for (int i = 0; i < n; i++) { 45 if (board[i][col] == 'Q') 46 return false; 47 } 48 // 检查右上方是否有皇后互相冲突 49 for (int i = row - 1, j = col + 1; 50 i >= 0 && j < n; i--, j++) { 51 if (board[i][j] == 'Q') 52 return false; 53 } 54 // 检查左上方是否有皇后互相冲突 55 for (int i = row - 1, j = col - 1; 56 i >= 0 && j >= 0; i--, j--) { 57 if (board[i][j] == 'Q') 58 return false; 59 } 60 return true; 61 } 62 };