36.需要再做
- 下标才是真正判断依据。
- 分配或者藏在了下标矩阵里。
- 初始状态一定要,下标有9这个数字,[9][10]
- i, j确实是列,[num]这里表示第几个??0-9一共十种情况?
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int row[9][10] = {0};
int col[9][10] = {0};
int box[9][10] = {0}; // 判断的时候不搞九宫格,全真的
for(int i=0; i<board.size(); i++){
for(int j=0; j<9; j++){
if(board[i][j]=='.') continue;
int num = board[i][j]-'0';
if(row[i][num]) return false;
if(col[j][num]) return false;
if(box[j/3+(i/3)*3][num]) return false;
row[i][num] = 1;
col[j][num] = 1;
box[j/3+(i/3)*3][num] = 1;
}
}
return true;
}
};
//改进后
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
bool row[9][10] = {false};
bool col[9][10] = {false};
bool box[9][10] = {false}; // 判断的时候不搞九宫格,全真的
for(int i=0; i<board.size(); i++){
for(int j=0; j<9; j++){
if(board[i][j]=='.') continue;
int num = board[i][j]-'0';
if(row[i][num] || col[j][num] || box[j/3+(i/3)*3][num]) return false;
row[i][num] = true;
col[j][num] = true;
box[j/3+(i/3)*3][num] = true;
}
}
return true;
}
};
37.
- 不理解i为何/3之后再*3??
- 存那些空的位置,要变换一下pair,注意次数只能那么多次,多了不行
- for循环去1-9,可以就添加数,作为一种情况,什么情况退出??回溯停止条件
- 必定bool返回
class Solution {
public:
void solveSudoku(vector<vector<char>>& board) {
for(int i=0; i<9; i++){
for(int j=0; j<9; j++){
if(board[i][j]=='.'){
dict.push_back(make_pair(i, j));
continue;
}
int num = board[i][j]-'1';
mRow[i][num] = true;
mCol[j][num] = true;
mBox[j/3 + i/3*3][num] = true;
}
}
dfs(board, 0);
}
bool dfs(vector<vector<char>>& board, int index){
if(index == dict.size()) return true;//只有这里return一个true
int r = dict[index].first;
int c = dict[index].second;
for(int i=0; i<9; i++){
if(!mRow[r][i] && !mCol[c][i] && !mBox[c/3 + r/3*3][i]){//全都查无此人?
board[r][c] = (char)(i+1); //这里煤气效果
mRow[r][i] = true;
mCol[c][i] = true;
mBox[c/3 + r/3*3][i] = true;
if(dfs(board, index+1)) return true; //回溯,发现一个答案就返回
mRow[r][i] = false;
mCol[c][i] = false;
mBox[c/3 + r/3*3][i] = false;
}
}
return false;
}
private:
bool mRow[9][9] = {false};
bool mCol[9][9] = {false};
bool mBox[9][9] = {false};
vector<pair<int, int>> dict;
};
52.
class Solution {
public:
//递归:遍历每一行,用vector<int> n保存行为i 时的皇后在j列,不能放在斜边:即新的x - y != 已经存在的i - j || (x + y != i + j)
vector<int> v;
unordered_map<int, int> m; //保存用过的列
int ans = 0;
void dfs(int row, int n) {
if (row > n) {
ans++;
return;
}
for (int j = 1; j <= n; j++) { //row行的列
if (m.find(j) == m.end()) {
int condition = 1;
for (int k = 1; k < row; k++) {
if (k - v[k] == row - j || k + v[k] == row + j) { //验证对角线是否满足条件
condition = 0;
break;
}
}
if (condition == 1) {
v[row] = j;
m[j]++;
dfs(row + 1, n);
v[row] = 0; // 回溯
m.erase(j); // 删除key为j的map?
}
}
}
}
int totalNQueens(int n) {
v.resize(n + 5);
dfs(1, n);
return ans;
}
//time : n*n* 不过时间复杂度好像不会算...........
//space : n 疑惑这个哈希表的空间复杂度?
};
51.
class Solution {
public:
vector<vector<string>> ans;
vector<vector<string>> solveNQueens(int n) {
vector<string> board(n, string(n, '.')); //可以这样子初始化吗
vector<bool> column(n, false), ldiag(2*n -1, false), rdiag(2*n-1, false);
if(n == 0) return ans;
backtracking(board,column, ldiag, rdiag, 0, n);
return ans;
}
void backtracking(vector<string> &board, vector<bool> &column, vector<bool> &ldiag, vector<bool> &rdiag, int row, int n){
if(row == n) {
ans.push_back(board);
return;
}
for(int i=0; i< n; i++){
if(column[i] || ldiag[n-row+i-1] || rdiag[row+i+1]) continue;
board[row][i] = 'Q';
column[i] = ldiag[n-row +i-1]= rdiag[row+i+1] = true;
backtracking(board, column, ldiag, rdiag, row+1, n);
board[row][i] = '.';
column[i] = ldiag[n-row +i-1]= rdiag[row+i+1] = false;
}
}
};
401.
hour总共四种,minutes也总共几种,59以内,都是可以直接胖丁的。
string to_string() + ':' +
回溯问题思路?
- 画出递归树?
- 找结束条件
- 找选择列表
- 路径有无重复,要剪枝吗?
- 做出选择,
- 撤销选择
78.
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> subsets(vector<int>& nums) {
backtracking(nums, 0);
return ans;
}
void backtracking(vector<int>& nums, int start){
if(start > nums.size()){
return;
}
ans.push_back(path);
for(int i = start; i<nums.size(); i++){
path.push_back(nums[i]);
backtracking(nums, i+1);
path.pop_back();
}
}
};
90.
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end());
backtracking(nums, 0);
return ans;
}
void backtracking(vector<int>& nums, int start){
if(start>nums.size()) return;
ans.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]);
backtracking(nums, i+1);
path.pop_back();
}
}
};
39.
- 在何处开始剪枝你不必担心。选择内外都可以。
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
backtracking(candidates, target, 0, 0);
return ans;
}
void backtracking(vector<int>& candidates, int target, int start, int sum){
if(sum == target){
ans.push_back(path);
return;
}
if(sum > target) return;
for(int i=start; i<candidates.size(); i++){
path.push_back(candidates[i]);
sum += candidates[i];
backtracking(candidates, target, i, sum);
path.pop_back();
sum -= candidates[i];
}
}
};
不如上一个
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
backtracking(candidates, target, 0, 0);
return ans;
}
void backtracking(vector<int>& candidates, int target, int start, int sum){
if(sum == target){
ans.push_back(path);
return;
}
//if(sum > target) return;
for(int i=start; i<candidates.size(); i++){
if(sum > target) continue;
path.push_back(candidates[i]);
sum += candidates[i];
backtracking(candidates, target, i, sum);
path.pop_back();
sum -= candidates[i];
}
}
};
46.
- 使用find结合vector,可能比再用used进行bool标记效果更好。
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> permute(vector<int>& nums) {
backtracking(nums, 0);
return ans;
}
void backtracking(vector<int>& nums, int index){
if(index == nums.size()){
ans.push_back(path);
}
for(int i=0; i<nums.size(); i++){
if(find(path.begin(), path.end(), nums[i])==path.end()){
path.push_back(nums[i]);
backtracking(nums, index+1);
path.pop_back();
}
}
}
};
再次总结:“排列”类型问题和“子集、组合”问题不同在于:“排列”问题使用used数组来标识选择列表,而“子集、组合”问题则使用start参数。另外还需注意两种问题的判重剪枝!
47.组合类问题?
未用used,未解出来??
语义为:当i可以选第一个元素之后的元素时(因为如果i=0,即只有一个元素,哪来的重复?有重复即说明起码有两个元素或以上,i>0),然后判断当前元素是否和上一个元素相同?如果相同,再判断上一个元素是否能用?
1223,比如第二个2, used==false,就没用过,现在到第三个==true,用它是可以的,不必continue
上一个元素不能用是??
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
backtracking(nums, 0);
return ans;
}
void backtracking(vector<int>& nums, int index){
if(path.size() == nums.size()){
ans.push_back(path);
return;
}
for(int i =0; i<nums.size(); i++){
if(find(path.begin(), path.end(), nums[i]) == path.end()){
if(i!=0 && nums[i]==nums[i-1]){
continue;
}
path.push_back(nums[i]);
backtracking(nums, index+1);
path.pop_back();
}
}
}
};
正解
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<bool> used; //但这里used这个数组??里面有重复数字的,如何区分
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
used.resize(nums.size());
backtracking(nums);
return ans;
}
void backtracking(vector<int>& nums){
if(path.size() == nums.size()){
ans.push_back(path);
return;
}
for(int i =0; i<nums.size(); i++){
if(used[i]==false){
if(i>0 && nums[i]==nums[i-1] && used[i-1]==false){//相同且上一个false,确实是回溯了,说明已经用过一次了
continue;
}
path.push_back(nums[i]);
used[i] = true;
backtracking(nums);
path.pop_back();
used[i] = false;
}
}
}
};
//与前一个字母相等,且前一个字母标记为false,说明前一个字母已经被回溯过,同级,不能再使用当前字母,否则重复
if(i>0 && !used[i] && s.charAt(i)==s.charAt(i-1) && !used[i-1]) continue;
这种出错原因是弄错全局变量了吗?
错误代码:used这个数组初始化失败?
class Solution {
public:
vector<string> res;
vector<bool> used; //string给你的那个而言的
//string path;
void backtrack(string s, string path)//used数组
{
if(path.size()==s.size())
{
res.push_back(path);
return;
}
for(int i=0;i<s.size();i++)
{
if(used[i] == false)
{
if(i>=1 &&s[i-1]==s[i]&& !used[i-1])//判重剪枝
continue;
path.push_back(s[i]);
used[i]=true;
backtrack(s, path);
used[i]=false;
path.pop_back();
}
}
}
vector<string> permutation(string s) {
if(s.size()==0)
return{};
sort(s.begin(),s.end());
string path = "";
vector<bool>used(s.size(), false);
backtrack(s, path);
return res;
}
};
class Solution {
public:
vector<string> res;
//vector<bool> used; //string给你的那个而言的
//string path;
void backtrack(string s, string path, vector<bool> used)//used数组
{
if(path.size()==s.size())
{
res.push_back(path);
return;
}
for(int i=0;i<s.size();i++)
{
if(used[i] == false)
{
if(i>=1 &&s[i-1]==s[i]&& !used[i-1])//判重剪枝
continue;
path.push_back(s[i]);
used[i]=true;
backtrack(s, path, used);
used[i]=false;
path.pop_back();
}
}
}
vector<string> permutation(string s) {
if(s.size()==0)
return{};
sort(s.begin(),s.end());
string path = "";
vector<bool>used(s.size(), false);
backtrack(s, path, used);
return res;
}
};
这种初始化的方式是错误的!!!
这个vector<bool> 在全局中也定义了
{vector<bool>used(s.size(), false);}
used.resize(s.size());
这个是对的,而且它默认就是全false,都不设定,
memset你可用
不能使用memset初始化复杂数组!!
像vector,string这中复杂数据类型不能直接memset, 会破快它的内部结构
vector初始化方式?
则:尽量使用reserve来减少不必要的内存分配次数。
原则:尽量使用empty而不是size()==0 来判断容器是否为空
?通过insert初始化?
int a[6] = {6,6,6,6,6,6};
vector<int> b;
//将a的所有元素插入到b中
b.insert(b.begin(), a, a+7);
401.
答案的顺序问题??
vector<string>res;
unordered_map<int,int> hash={{0,1},{1,2},{2,4},{3,8},{4,1},{5,2},{6,4},{7,8},{8,16},{9,32}};
void backtrack(int num,int start,pair<int,int>& time)
{
if(num==0)
{
if(time.first>11||time.second>59)//判断合法性
return;
string temp_hour=to_string(time.first);
string temp_minute=to_string(time.second);
if(temp_minute.size()==1)//如果minute只有一位要补0
temp_minute.insert(0,"0");
res.push_back(temp_hour+":"+temp_minute);//构造格式
return;
}
for(int i=start;i<10;i++)
{
if(time.first>11||time.second>59)
continue;
pair<int,int>store=time;//保存状态
if(i<4)
time.first+=hash[i];
else
time.second+=hash[i];
backtrack(num-1,i+1,time);//进入下一层,注意下一层的start是i+1,即从当前灯的下一盏开始
time=store;//恢复状态
}
}
vector<string> readBinaryWatch(int num) {
pair<int,int>time(0,0);//初始化时间为0:00
backtrack(num,0,time);
return res;
}
我的代码:
class Solution {
public:
vector<string> ans;
//string path;
// string hour;
// string minute;
pair<int, int> time;
unordered_map<int, int> dict = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 1}, {5, 2}, {6, 4}, {7, 8}, {8, 16}, {9, 16}, {10, 32}};
vector<string> readBinaryWatch(int turnedOn) {
backtracking(turnedOn, 0);
return ans;
}
void backtracking(int turnedOn, int start){
if(turnedOn == 0){
if(time.first> 11 || time.second>59){
return;
}
string h = to_string(time.first);
string m = to_string(time.second);
if(m.size() == 1) m.insert(0,"0");
ans.push_back(h+':'+m);
return;
}
for(int i=start; i<10; i++){
if(time.first> 11 || time.second>59) continue; //这里我觉得用break更好
pair<int, int> tmp = time;
if(i<4){
time.first += dict[i];
}else{
time.second += dict[i];
}
backtracking(turnedOn-1, start+1);
time = tmp;
}
}
};
79.
- 忘了走过的路径要自己标记一下
- 为何这种做法出错??标记之后,当回溯的时候,要解除掉。
- 在主程序中做了判断,实际不需要这判断。
class Solution {
public:
bool ans = false;
bool exist(vector<vector<char>>& board, string word) {
for(int i=0; i<board.size(); i++){
for(int j=0; j<board[0].size(); j++){
if(board[i][j]==word[0]){
dfs(board, word, i, j,1);
}
}
}
return ans;
}
void dfs(vector<vector<char>>& board, string word, int r, int c,int index){
if(r<0 || r>board.size() || c<0 || c> board[0].size() || board[r][c] != '1') return;
if(index==word.size()){
ans = true;
return;
}
char tmp = board[r][c];
board[r][c] = '1';
if(board[r-1][c] == word[index]) dfs(board, word, r-1, c, index+1);
if(board[r+1][c] == word[index]) dfs(board, word, r+1, c, index+1);
if(board[r][c-1] == word[index]) dfs(board, word, r, c-1, index+1);
if(board[r][c+1] == word[index]) dfs(board, word, r, c+1, index+1);
board[r][c] = tmp;
}
};
c那里是可以整>=size(),注意等于这种勤快。
212.据说用前缀树???
- 使用了专用的vector<vector<bool>> used, 而不是修
- 注意一定要回溯,回归到false去,否则仅26通过。
used[i][j] = true;
dfs(board, word, i-1, j, index+1); //+1
dfs(board, word, i+1, j, index+1);
dfs(board, word, i, j-1, index+1);
dfs(board, word, i, j+1, index+1);
//used[i][j] = false;
class Solution {
public:
vector<string> ans;
string path;
int m, n;
vector<vector<bool>> used;
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
m = board.size();
n = board[0].size();
vector<bool> tmp(n, false);
used.resize(m,tmp);
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){//对每个word来一次?
for(int k = 0; k<words.size(); k++){
dfs(board, words[k], i, j, 0);
}
}
}
return ans;
}
void dfs(vector<vector<char>>& board, string& word, int i, int j, int index){
if(index == word.size()){
if(find(ans.begin(), ans.end(), word) == ans.end()) ans.push_back(word); //我也不知怎么重复上了
return;
}
if(i<0 || i>=board.size() || j<0 || j>=board[0].size() || used[i][j]==true || board[i][j]!= word[index]){
return;
}
used[i][j] = true;
dfs(board, word, i-1, j, index+1); //+1
dfs(board, word, i+1, j, index+1);
dfs(board, word, i, j-1, index+1);
dfs(board, word, i, j+1, index+1);
used[i][j] = false;
}
};
if(r<0 || r>=board.size() || c<0 || c>= board[0].size() || board[r][c] != '1' || board[r][c]!=word[index] || find == true) return;
二维的如何resize一下???
vector <vector<int> > v1;
vector<int> temp(4)
v1.resize(5,temp)
313.
- 注意substr(0,i+1)和substr(i+1)这两个才是连接
- 对接下来东西也用同样的方法,就是迭代dfs
- 保证左边全为回文,才往里一个个放,最终的也是回文。
class Solution {
public:
vector<vector<string>> ans;
vector<string> path;
vector<vector<string>> partition(string s) {
backtracking(s);
return ans;
}
void backtracking(string s){
if(s == ""){
ans.push_back(path);
return;
}
for(int i=0; i<s.size(); i++){
string ls = s.substr(0, i+1);
string rs = s.substr(i+1);
if(isHuiWen(ls)){
path.push_back(ls);
backtracking(rs);
path.pop_back();
}
}
}
bool isHuiWen(string s){
int l = 0;
int r = s.size()-1;
while(l<r){
if(s[l] != s[r]) return false;
l++;
r--;
}
return true;
}
};