1: 78. 子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
class Solution {
public:
// 回溯算法的核心:就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」
// 在决策树上dfs
vector<vector<int>> res;
vector<vector<int>> subsets(vector<int>& nums) {
vector<int> track;
backtrace(nums,0,track);
return res;
}
void backtrace(vector<int> &nums, int start, vector<int> &track){
//每次记录结果。
res.push_back(track);
for(int i = start; i<nums.size(); ++i){ //因为是子集,那么每次需要从下一个元素接着做选择
//做选择
track.push_back(nums[i]);
//从下一个元素接着做选择
backtrace(nums,i+1,track);
//撤销选择
track.pop_back();
}
}
};
2:77. 组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> combine(int n, int k) {
vector<int> track;
backtrace(n,k,1,track);
return res;
}
void backtrace(int n, int k, int start,vector<int> &track){
if(track.size() == k){
res.push_back(track);
return;
}
for(int i = start; i<=n; i++){
track.push_back(i); //做选择
backtrace(n,k,i+1,track); //组合与顺序无关,所以需要在下一个元素上接着选择
track.pop_back(); //撤销选择
}
}
};
3:46. 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> permute(vector<int>& nums) {
set<int> mem;
vector<int> tmp;
backtrack(nums,mem,tmp);
return res;
}
void backtrack(vector<int> &nums, set<int> &mem, vector<int> &tmp){
if(mem.size() == nums.size())
{
//用集合作为返回的值具有很大的问题,因为set是有序的,这样写之后所有的结果都是一样的
//res.push_back(vector<int>(mem.begin(),mem.end()))
res.push_back(tmp);
//cout<<endl;
return;
}
for(int i = 0; i<nums.size(); ++i){
if(mem.count(nums[i])) continue;
//做选择
mem.insert(nums[i]);
tmp.push_back(nums[i]);
backtrack(nums,mem,tmp);
//撤销选择
mem.erase(nums[i]);
tmp.pop_back();
//cout<<*mem.begin()<<" "<<*mem.end()<<endl;
}
return;
}
};
4:37. 解数独
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
class Solution {
public:
string nums = "123456789";
void solveSudoku(vector<vector<char>>& board) {
backtrace(board,0,0);
}
//判断字符是否合法
bool isvalid(vector<vector<char>> &board,int r, int c, char n){
//判断每一行或者每一列是否有chr数字
for(int k = 0; k<9; k++){
if(board[r][k] == n) return false;
if(board[k][c] == n) return false;
}
//判断小方格里面有没有相同的数字
int x = r/3, y = c/3;
for(int k = 0; k<3; ++k)
for(int m = 0; m<3; ++m){
if(board[3*x+k][3*y+m] == n) return false;
}
return true;
}
//用bool作为返回值,因为只需要找到一个可行解,利用返回值来使得找到之后就退出
bool backtrace(vector<vector<char>> &board, int r, int c){
int m = 9, n = 9;
if(c == n){ //如果列越界,那么从下一行开始
return backtrace(board,r+1,0);
}
//r越界,说明遍历完了
if(r == m) return true;
for(int i = r; i<m; ++i){
for(int j = c; j<n; ++j){
if(board[i][j] != '.') return backtrace(board,i,j+1); //如果已经填好了,那么不用我们穷举数字了,这里不能用continue。。。
//设想,如果某一行的最后一个元素不是'.',如果continue的话,那么将会从下一行的c列开始,而不是第0列
//做选择,对于每一个位置,尝试着找一个字符填入进去
for(char ch = '1'; ch <= '9'; ch++){
if(!isvalid(board,i,j,ch)) continue; //不合法的数字
board[i][j] = ch;
if(backtrace(board,i,j+1)) //按照行递增找,如果j+1超出了边界,那么从i+1开始
return true;
board[i][j] = '.';
}
//穷举完 1~9 还没有true,此路不通
return false;
}
}
return false;
}
};
5:22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
class Solution {
public:
vector<string> res;
vector<string> generateParenthesis(int n) {
string track;
help(n,track,0,0,0);
return res;
}
// l:左括号个数 r:右括号次数
// sum:每次加一个左括号,sum++, 每加一个右括号 sum--
void help(int n,string track,int l, int r, int sum){
//base case
if(sum < 0 || l>n || r>n) return;
if(l == n && r==n && sum == 0){
res.push_back(track);
return;
}
for(int i = 0; i<2; ++i){
string tmp = track;
if(i == 0){ //选择左括号
track += '(';
help(n,track,l+1,r,sum+1);
}else{ //选择右括号
track += ')';
help(n,track,l,r+1,sum-1);
}
track = tmp; //撤销选择
}//for
}
};
6:51. N 皇后
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
class Solution {
public:
vector<vector<string>> res;
vector<vector<string>> solveNQueens(int n) {
//初始化一个全是 '.' 的字符串列表
string s;
for(int i = 0; i<n; ++i)
s +='.';
vector<string> board(n,s);
help(board,n,0);
return res;
}
//判断对应位置是否是合法的,这里需要注意一个问题:
//1.只需要检查列是否有Q即可
//2.对角线有两条:左上方和右上方
bool isvalid(vector<string> &board, int r, int c, int n){
//检查列
for(int i = 0; i<r; ++i){
if(board[i][c] == 'Q') return false;
}
//判断左上方是否有Q
int tmpr = r, tmpc = c;
while(tmpr>=0 && tmpc >=0){
if(board[tmpr][tmpc] == 'Q') return false;
tmpr--; tmpc--;
}
//判断右上方是否有Q
tmpr = r; tmpc = c;
while(tmpr>=0 && tmpc<n){
if(board[tmpr][tmpc] == 'Q') return false;
tmpr--; tmpc++;
}
return true;
}
void help(vector<string> &board, int n,int r){
//base case
if(r == n){
res.push_back(board);
return;
}
for(int j = 0; j<n; ++j){ //某一列
if(!isvalid(board,r,j,n)) continue; //如果不合法跳过
board[r][j] = 'Q'; //选择
help(board,n,r+1); //下一行
board[r][j] = '.'; //撤销选择
}
}
};
7: