回溯算法的框架 解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
如果你不理解这三个词语的解释,没关系,我们后面会用「全排列」和「N皇后问题」这两个经典的回溯算法问题来帮你理解这些词语是什么意思,现在你先留着印象。
代码方面,回溯算法的框架:
1 2 3 4 5 6 7 8 9 10 result = [] def backtrack (路径, 选择列表 ): if 满足结束条件: result.add(路径) return for 选择 in 选择列表: 做选择 backtrack(路径, 选择列表) 撤销选择
其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」,特别简单。
全排列问题 Leetcode:46.全排列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 vector<vector<int >> ret; void backtrack (vector<int > &path, vector<int > &nums) { if (path.size () == nums.size ()) { ret.push_back (path); return ; } for (int num : nums) { if (find (path.begin (), path.end (), num) != path.end ()) { continue ; } path.push_back (num); backtrack (path, nums); path.pop_back (); } } vector<vector<int >> permute (vector<int >& nums) { vector<int > path; backtrack (path, nums); return ret; }
N 皇后问题 N 皇后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 vector<vector<string>> res; vector<vector<string>> solveNQueens (int n) { vector<string> board (n, string(n, '.' )) ; backtrack (board, 0 ); return res; } void backtrack (vector<string>& board, int row) { if (row == board.size ()) { res.push_back (board); return ; } for (int col = 0 ; col < board[row].size (); col++) { if (!isValid (board, row, col)) continue ; board[row][col] = 'Q' ; backtrack (board, row + 1 ); board[row][col] = '.' ; } } bool isValid (vector<string>& board, int row, int col) { int n = board.size (); for (int i = 0 ; i <= row; i++) { if (board[i][col] == 'Q' ) return false ; } for (int i = row - 1 , j = col + 1 ; i >= 0 && j < n; i--, j++) { if (board[i][j] == 'Q' ) return false ; } for (int i = row - 1 , j = col - 1 ; i >= 0 && j >= 0 ; i--, j--) { if (board[i][j] == 'Q' ) return false ; } return true ; }
字母大小全排列 784. 字母大小写全排列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 vector<string> ret; void backtrack (string path, string S) { if (path.size () == S.size ()) { ret.push_back (path); return ; } char c = S[path.size ()]; path += c; backtrack (path, S); if (isalpha (c)) { path = path.substr (0 , path.size ()-1 ); if (c >= 'A' && c <= 'Z' ) { path += tolower (c); } else { path += toupper (c); } backtrack (path, S); } } vector<string> letterCasePermutation (string S) { ret = {}; if (S.size () == 0 ) return ret; string path; backtrack (path, S); return ret; }
参考