日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【C++】【学习笔记】【递归与回溯问题详解与例题】排列问题;组合问题;二维平面回溯;flood fill问题;搜索问题(八皇后);

發布時間:2024/3/24 c/c++ 32 豆豆

目錄

  • 七、遞歸和回溯
    • 1、回溯
    • 2、回溯應用 - 排列問題
    • 2、回溯應用 - 組合問題
    • 3、回溯應用 - 二維平面
    • 4、回溯應用 - floodfill算法 問題
    • 4、回溯應用 - 搜索問題 - 八皇后
  • 參考

七、遞歸和回溯

  • 結局問題的思路普遍還是樹形問題;
  • 寫遞歸的時候,心里要有一個遞歸樹
  • 遞歸調用嘗試找答案的問題稱作回溯

1、回溯

17. 電話號碼的字母組合

  • 這種題沒什么意義,不用復習 O(3^n) = O(2 ^n)指數級;
class Solution { public:vector<vector<char>> map;void dfs(string &digits,int index,vector<string> &res,string str){if(index>digits.size()) return;if(index == digits.size()){if(str!="")res.push_back(str);return;}int index_m = digits[index]-'2';for(int i=0;i<map[index_m].size();i++)dfs(digits,index+1,res,str+map[index_m][i]);}vector<string> letterCombinations(string digits) {vector<string> res;map = {{'a','b','c'},{'d','e','f'},{'g','h','i'},{'j','k','l'},{'m','n','o'},{'p','q','r','s'},{'t','u','v'},{'w','x','y','z'}};dfs(digits,0,res,"");return res;} };

93. 復原 IP 地址

  • dfs
class Solution { public:vector<string> res;void dfs(const string& s,int index,int nums,string str){if(nums==4&&index ==s.size()){res.push_back(str.substr(0,str.size()-1));return ;}if(index>=s.size()) return ;if(nums>3) return;int temp2 = stoi(s.substr(index,2));int temp3 = stoi(s.substr(index,3));dfs(s,index+1,nums+1,str+s[index]+".");if(s[index]!='0')dfs(s,index+2,nums+1,str+s.substr(index,2)+".");if(s[index]!='0'&& temp3>0&&temp3<256)dfs(s,index+3,nums+1,str+s.substr(index,3)+".");}vector<string> restoreIpAddresses(string s) {dfs(s,0,0,"");return res;} };

131. 分割回文串

  • dfs 回溯;對于判斷回文,可以用記憶化,算的時候,記下來那部分算過;
class Solution { private:bool ispalindrome(const string &str){for (int i = 0, j = str.size()-1; i < j; i++,j--)if (str[i] != str[j])return false;return true;}void dfs(string &s, int index, vector<string> &temp){if (index == s.size())res.push_back(temp);if (index >= s.size()) return;for (int i = index; i < s.size(); i++){string m_str = s.substr(index, i - index + 1);if (ispalindrome(m_str)){temp.push_back(m_str);dfs(s, i + 1, temp);temp.pop_back();}}}vector<vector<string>> res; public:vector<vector<string>> partition(string s) {vector<string> temp;dfs(s, 0,temp);return res;} };

2、回溯應用 - 排列問題

46. 全排列

  • 回溯-hash記錄-想象遞歸樹即可;
class Solution { public:vector<vector<int>> res;vector<bool> mash_;void dfs(const vector<int>& nums,vector<int>& temp,int index){if(index == nums.size()){res.push_back(temp);return ;}for(int i=0;i<nums.size();i++){if(!mash_[i]){mash_[i] =true;temp[index] = (nums[i]);dfs(nums,temp,index+1);mash_[i] = false;}}}vector<vector<int>> permute(vector<int>& nums) {mash_ = vector<bool>(nums.size(),false);vector<int> temp(nums.size(),0);dfs(nums,temp,0);return res;} };

47. 全排列 II

  • 相對上面的,加上了用hashset 記錄當前index用過的數字
  • 或者直接判斷和上一個一不一樣;一樣就跳過
class Solution { public:vector<unordered_set<int>> _hash;vector<vector<int>> res;vector<bool> _hash_bo;void dfs(vector<int>& nums,int index,vector<int>& temp){if(index ==nums.size()){res.push_back(temp);return;}for(int i= 0;i<nums.size();i++){if(!_hash_bo[i]){if(_hash[index].find(nums[i]) == _hash[index].end()){temp[index] = nums[i];_hash[index].insert(nums[i]);_hash_bo[i] = true;dfs(nums,index+1,temp);_hash_bo[i] = false;}}}_hash[index].clear();}vector<vector<int>> permuteUnique(vector<int>& nums) {_hash = vector<unordered_set<int>>(nums.size());vector<int> temp(nums.size());_hash_bo = vector<bool>(nums.size(),false);dfs(nums,0,temp);return res;} };
  • 排序后dfs;用與前一個重復數是否相等來判斷剪枝

2、回溯應用 - 組合問題

77. 組合

  • 回溯、剪枝
class Solution { public:void dfs(vector<int> &temp,int index,const int nums,const int &n,const int & k) {if(index ==k) res.push_back(temp);if(index >=k) return;if(k-index-1>n-nums) return;for(int i=nums;i<=n;i++){temp[index] = i;dfs(temp,index+1,i+1,n,k);}}vector<vector<int>> res;vector<vector<int>> combine(int n, int k) {vector<int> temp(k,0);dfs(temp,0,1,n,k);return res;} };

39. 組合總和

  • 回溯、剪枝;但是為了不能重復 ;所以索引和之前的索引一致
class Solution { public:vector<vector<int>> res;vector<int> temp;void dfs(vector<int>& candidates,const int& target,int sum,int index){if(sum ==target) {res.push_back(temp);return ;}if(index>=candidates.size()) return ;for(int i=index;i<candidates.size();i++){if(sum+candidates[i]<=target){temp.push_back(candidates[i]);dfs(candidates,target,sum+candidates[i],i);temp.pop_back();}}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {dfs(candidates,target,0,0);return res;} };

40. 組合總和 II

  • 回溯、剪枝;但是為了不能重復 ;所以哈希做一下這一位不能用之前用過的
  • 或者用判斷和前一個一不一樣即可;
class Solution { public:vector<vector<int>> res;vector<int> temp;void dfs(vector<int>& candidates,const int& target,int sum,int index){if(sum ==target) {res.push_back(temp);return ;}if(index>=candidates.size()) return ;unordered_set<int> _hash;for(int i=index;i<candidates.size();i++){//if(sum+candidates[i]<=target&&(i>index&&candidates[i]!=candidates[i-1]||i ==index))if(sum+candidates[i]<=target&&_hash.find(candidates[i])==_hash.end()){_hash.insert(candidates[i]);temp.push_back(candidates[i]);dfs(candidates,target,sum+candidates[i],i+1);temp.pop_back();}}}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {sort(candidates.begin(),candidates.end());dfs(candidates,target,0,0);return res;} };

216. 組合總和 III

  • 回溯、剪枝;反而更簡單了
class Solution { public:vector<vector<int>> res;vector<int> temp;void dfs(const int &k,int index,int n,int i_vec){if(index ==k&& n ==0){res.push_back(temp);return;}if(index>=k) return ;for(int i=i_vec;i<10;i++){if(n-i>=0){temp[index] = i;dfs(k,index+1,n-i,i+1);}}}vector<vector<int>> combinationSum3(int k, int n) {temp = vector<int>(k,0);dfs(k,0,n,1);return res;} };

78. 子集

class Solution { public:vector<int> temp;vector<vector<int>> res;void dfs(vector<int>& nums,int index){if(index == nums.size()){res.push_back(temp);return;}dfs(nums,index+1);temp.push_back(nums[index]);dfs(nums,index+1);temp.pop_back();}vector<vector<int>> subsets(vector<int>& nums) {dfs(nums,0);return res;} };

90. 子集 II 類似40. 組合總和 II
*

class Solution { public:vector<vector<int>> res;vector<int> temp;void dfs(vector<int>& nums,int index,const int& len){if(temp.size() ==len) {res.push_back(temp);return ;}for(int i=index;i<nums.size();i++){if(i==index||(i>index && nums[i]!= nums[i-1])){temp.push_back(nums[i]);dfs(nums,i+1,len);temp.pop_back();}}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {int len = nums.size();sort(nums.begin(),nums.end());for(int i=0;i<=len;i++)dfs(nums,0,i);return res;} };
  • 按同層上次有沒有用到來dfs
  • used_pre == false 代表同層 用過了,我這支分支就不能用了
  • 也就是說,對于當前選擇的數 x,若前面有與其相同的數 y,且沒有選擇 y,此時包含 x 的子集,必然會出現在包含 y 的所有子集中。
  • 和上面的方法,扔掉的分支相同;
class Solution { public:vector<vector<int>> res;vector<int> temp;void dfs(vector<int>& nums,int index,bool used_pre){if(index ==nums.size()) {res.push_back(temp);return;}if(index>=nums.size()) return;dfs(nums,index+1,false);if((!used_pre)&&index>0&&nums[index] ==nums[index-1]) return ;temp.push_back(nums[index]);dfs(nums,index+1,true);temp.pop_back();}vector<vector<int>> subsetsWithDup(vector<int>& nums) {sort(nums.begin(),nums.end());dfs(nums,0,false);return res;} };

401. 二進制手表 //*

  • 我用了遞歸回溯
  • 官方為枚舉判斷。
class Solution { public:vector<string> res;void result_cal(pair<int,int> nums){if(nums.first>11) return;if(nums.second>59) return;string _l = to_string(nums.first);_l +=":";if(nums.second<10) _l += "0";_l += to_string(nums.second);res.push_back(_l);}void dfs(int turnedOn,int index,pair<int,int> nums){if(index==10&& turnedOn ==0){result_cal(nums);return;}if(index>=10) return;dfs(turnedOn,index+1,nums);if(turnedOn>0){if(index<6)nums.second+=1<<index;elsenums.first +=1<<(index-6);dfs(turnedOn-1,index+1,nums);if(index<6)nums.second^=1<<index;elsenums.first ^=1<<(index-6);}}vector<string> readBinaryWatch(int turnedOn) {dfs(turnedOn,0,{0,0});return res;} };

3、回溯應用 - 二維平面

79. 單詞搜索

  • 暴力dfs + 預處理;
  • 遞歸中經典的先污染后治理方案;
class Solution { public:vector<vector<bool>> _used;bool dfs(vector<vector<char>>& board, string word,int word_index,int x,int y){if(x<0||y<0||x>=board[0].size()||y>=board.size()) return false;if(_used[y][x]) return false;if(word_index>=word.size()) return false;if(board[y][x]!=word[word_index]) return false;if(word_index ==word.size()-1&&board[y][x]==word[word_index]) return true;_used[y][x] = true;bool _t = dfs(board,word,word_index+1,x+1,y)||dfs(board,word,word_index+1,x,y+1)||dfs(board,word,word_index+1,x,y-1)||dfs(board,word,word_index+1,x-1,y);_used[y][x] = false;return _t;}bool exist(vector<vector<char>>& board, string word) {int len1 = board.size();int len2 = board[0].size();unordered_set<char> _hash;for(auto &vec:board)for(auto &it:vec)_hash.insert(it);for(auto &it:word)if(_hash.find(it)==_hash.end())return false;_used =vector<vector<bool>>(len1,vector<bool>(len2,false));for(int i=0;i<len1;i++){for(int j=0;j<len2;j++){if(dfs(board,word,0,j,i))return true;}}return false;} };

4、回溯應用 - floodfill算法 問題

200. 島嶼數量

  • 標準 floodfill
class Solution { public:vector<vector<bool>> _map;int dfs(vector<vector<char>>& grid,int x,int y){if(x<0||y<0||x>=grid.size()||y>=grid.back().size()) return 0;if(grid[x][y] == '0') return 0;if(_map[x][y]) return 0;_map[x][y] = true;return dfs(grid,x,y+1)+dfs(grid,x+1,y)+dfs(grid,x,y)+dfs(grid,x-1,y)+dfs(grid,x,y-1)+1;}int numIslands(vector<vector<char>>& grid) {int res = 0;_map = vector<vector<bool>>(grid.size(),vector<bool>(grid[0].size(),false));for(int i=0;i<grid.size();i++)for(int j=0;j<grid.back().size();j++)if(dfs(grid,i,j)) res++;return res;} };

130. 被圍繞的區域

  • 標準 floodfill ;思維反向一下;從外部開始dfs
class Solution { public: vector<vector<bool>> _map;void dfs(vector<vector<char>>& board, int x, int y){if (x < 0 || y < 0 || x >= board.size() || y >= board.back().size()) return ;if (board[x][y] == 'X'||_map[x][y]) return ;_map[x][y] = true;dfs(board,x,y+1);dfs(board,x+1,y);dfs(board,x,y-1);dfs(board,x-1,y);}void solve(vector<vector<char>>& board) {int len = board.size();int len2 = board.back().size();_map = vector<vector<bool>>(len, vector<bool>(len2, false));for(int i=0;i<len;i++){if(board[i][0] =='O')dfs(board,i,0);if(board[i][len2-1] =='O')dfs(board,i,len2-1);}for(int i=0;i<len2;i++){if(board[0][i] =='O')dfs(board,0,i);if(board[len-1][i] =='O')dfs(board,len-1,i);}for(int i=1;i<len-1;i++)for(int j=1;j<len2-1;j++){if(board[i][j] =='O'&&_map[i][j] !=1)board[i][j] ='X';}} };

417. 太平洋大西洋水流問題 //***

  • 第一次的做法太笨了
class Solution { public:int dir;int len;int len2;vector<vector<bool>> used;bool dfs(vector<vector<int>>& heights,int x,int y,vector<vector<bool>> &_map,int temp){if(dir ==-1){if(x<0||y<0) return true;if(x>=len||y>=len2) return false;}else{if(x<0||y<0) return false;if(x>=len||y>=len2) return true;}if(temp< heights[x][y]) return false;if(used[x][y]) return _map[x][y];used[x][y] = true;_map[x][y] =dfs(heights, x - 1, y, _map,heights[x][y])||dfs(heights, x, y - 1, _map,heights[x][y])||dfs(heights, x + 1, y, _map,heights[x][y])||dfs(heights, x, y + 1, _map,heights[x][y]);used[x][y] = false;// cout<<x<<" "<<y<<" : "<<_map[x][y] <<endl;return _map[x][y];}vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {len = heights.size();len2 = heights.back().size();vector<vector<bool>> _map(len,vector<bool>(len2,false));vector<vector<bool>> _map2(len,vector<bool>(len2,false));used = vector<vector<bool>>(len,vector<bool>(len2,false));vector<vector<int>> res;dir = -1;for(int i=0;i<len;i++)for(int j=0;j<len2;j++){dfs(heights,i,j,_map,heights[i][j]);}// used = vector<vector<bool>>(len,vector<bool>(len2,false));dir = 1;for(int i=len-1;i>=0;i--)for(int j = len2-1;j>=0;j--){dfs(heights,i,j,_map2,heights[i][j]);}for(int i=0;i<len;i++)for(int j=0;j<len2;j++){if(_map[i][j] ==true &&_map2[i][j] ==true )res.push_back({i,j});}return res;} };
  • 應該從邊緣往內dfs;和上一題一樣;簡單很多 ;時間快很多
class Solution { public: int len,len2;vector<vector<int>> dir = {{0,1},{1,0},{0,-1},{-1,0}};bool _fit(int &x,int y){if(x<0||y<0||x>=len||y>=len2) return false;return true;}void dfs(vector<vector<int>>& heights,int x,int y,vector<vector<bool>>& _map){if(_map[x][y]) return;_map[x][y] = true;for(int i=0;i<4;i++){int new_x = x+dir[i][0];int new_y = y+dir[i][1];if(_fit(new_x,new_y) && heights[x][y]<=heights[new_x][new_y])dfs(heights,new_x,new_y,_map);}}vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {len = heights.size();len2 = heights.back().size();vector<vector<bool>>_map1(len,vector<bool>(len2,false));vector<vector<bool>>_map2(len,vector<bool>(len2,false));vector<vector<int>> res;for(int i=0;i<len;i++){dfs(heights,i,0,_map1);dfs(heights,i,len2-1,_map2);}for(int i=0;i<len2;i++){dfs(heights,0,i,_map1);dfs(heights,len-1,i,_map2);}for(int i=0;i<len;i++)for(int j=0;j<len2;j++)if(_map1[i][j] ==true &&_map2[i][j] ==true )res.push_back({i,j});return res;} };

4、回溯應用 - 搜索問題 - 八皇后

  • 人工智能搜索問題基礎

51. N 皇后 同 面試題 08.12. 八皇后 //***這個思路牛逼啊

  • 其實就是枚舉每一行中皇后應該擺在哪里,然后有一定的剪枝;
  • 考慮 回溯;難點在于剪枝的判斷;
  • 這里判重剪枝的方法:用三個數組;依次表示所有列,所有右上到左下對角線,所有左上到右下對角線;
class Solution { public:vector<vector<string>> res;vector<bool> v_used;vector<bool> l_used;vector<bool> r_used;int _n;void dfs(int x,int y,vector<string>& temp){if(v_used[y]) return;if(l_used[x+y]) return;if(r_used[x-y+_n-1]) return;v_used[y] = true;l_used[x+y] = true;r_used[x-y+_n-1] = true;temp[x][y] = 'Q';// cout<<x<<" : "<<y<<endl;if(x == _n-1)res.push_back(temp);elsefor(int i=0;i<_n;i++)dfs(x+1,i,temp);v_used[y] = false;l_used[x+y] = false;r_used[x-y+_n-1] = false;temp[x][y] = '.';}vector<vector<string>> solveNQueens(int n) {string str_(n,'.');vector<string> temp(n,str_);_n = n;v_used = vector<bool> (n,false);l_used = vector<bool> (2*n-1,false); //i+jr_used = vector<bool> (2*n-1,false); //i-j +n-1for(int i=0;i<n;i++)dfs(0,i,temp);return res;} };

52. N皇后 II

  • 八皇后的位運算判斷法;利用位運算取代之前的數組;
class Solution { public:int column_;int l_;int r_;int res;void dfs(int x,int y,const int& n){if(column_& (1<<y)) return;if(l_& (1<<(x+y))) return;if(r_& (1<<(x-y+n-1))) return;column_ |= (1<<y);l_ |= 1<<(x+y);r_ |= 1<<(x-y+n-1);if(x ==n-1)res++;elsefor(int i=0;i<n;i++)dfs(x+1,i,n);column_ ^= (1<<y);l_ ^= 1<<(x+y);r_ ^= 1<<(x-y+n-1);}int totalNQueens(int n) {column_ = 0;l_ = 0;r_ = 0;res = 0;for(int i=0;i<n;i++)dfs(0,i,n);return res;} };

37. 解數獨

  • 數獨問題,會有專門的算法?;我就硬dfs做的
class Solution { public:int n;vector<int> _block;vector<int> _co;vector<int> _ro;int which_block(const int& i, const int& j){if (i < 3){if (j < 3)return 0;else if (j < 6)return 3;elsereturn 6;}else if (i < 6){if (j < 3)return 1;else if (j < 6)return 4;elsereturn 7;}else{if (j < 3)return 2;else if (j < 6)return 5;elsereturn 8;}}bool dfs(int x, int y, int val, vector<vector<char>>& board){// cout << x << " " << y << " : " << val << endl;if (board[x][y] != '.') return false;if (_ro[x] & (1 << val)) return false;if (_co[y] & (1 << val)) return false;if (_block[which_block(x, y)] & (1 << val)) return false;_ro[x] |= (1 << val);_co[y] |= (1 << val);_block[which_block(x, y)] |= (1 << val);board[x][y] = val + '0';int begin_i = -1, begin_j = -1;for (int i = x; i < n; i++){for (int j = 0; j < n; j++)if (board[i][j] == '.'){begin_i = i;begin_j = j;break;}if (begin_i != -1)break;}if (begin_i == -1)return true;for (int i = 1; i <= n; i++)if (dfs(begin_i, begin_j, i, board))return true;_ro[x] &= ~(1 << val);_co[y] &= ~(1 << val);_block[which_block(x, y)] &= ~(1 << val);board[x][y] = '.';return false;}void solveSudoku(vector<vector<char>>& board) {n = 9;_block = vector<int>(n, 0);_co = vector<int>(n, 0);_ro = vector<int>(n, 0);for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){if (board[i][j] != '.'){int temp = board[i][j] - '0';_ro[i] |= (1 << temp);_co[j] |= (1 << temp);_block[which_block(i, j)] |= (1 << temp);}}}int begin_i = -1, begin_j = -1;for (int i = 0; i < n; i++){for (int j = 0; j < n; j++)if (board[i][j] == '.'){begin_i = i;begin_j = j;break;}if (begin_i != -1)break;}for (int i = 1; i <= n; i++)if (dfs(begin_i, begin_j, i, board))return;} };

參考

【學習筆記】【C++】【Leetcode 分門別類講解】

總結

以上是生活随笔為你收集整理的【C++】【学习笔记】【递归与回溯问题详解与例题】排列问题;组合问题;二维平面回溯;flood fill问题;搜索问题(八皇后);的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。