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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法题复习(回溯)

發(fā)布時間:2023/12/1 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法题复习(回溯) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • base code
  • 棋盤問題
    • 51. N 皇后
    • 37. 解數(shù)獨(dú)
  • 組合問題
    • 77. 組合
      • 未剪枝優(yōu)化
      • 剪枝優(yōu)化
    • 216. 組合總和 III
      • 未剪枝優(yōu)化
      • 剪枝優(yōu)化
    • 17. 電話號碼的字母組合
    • 39. 組合總和
      • 未剪枝優(yōu)化
      • 剪枝優(yōu)化
    • 40. 組合總和 II,挺重要的,涉及到去重了
  • 切割問題
    • 131. 分割回文串
  • 子集問題
    • 78. 子集
    • 90. 子集 II,有樹層去重
    • 491. 遞增子序列,也需要數(shù)層去重,使用unordered_set
  • 排列問題
    • 46. 全排列
    • 47. 全排列 II + 數(shù)層去重
  • 行程安排+高級容器的使用,現(xiàn)在還沒吃透

base code

回溯法大致思路

void backtracking(參數(shù)) {if(終止條件) {存放結(jié)果;return;}for(選擇:本層集合中的元素(樹中節(jié)點孩子的數(shù)量就是集合的大小)) {處理節(jié)點;backtracking(路徑,選擇列表); //遞歸回溯,撤銷處理結(jié)果} }

棋盤問題

51. N 皇后

https://leetcode-cn.com/problems/n-queens/

class Solution { private:vector<vector<string>> result; public:bool isValid(int row, int col, vector<string>& chessboard, int n){//檢查列for(int i = 0; i < row; i++){if(chessboard[i][col] == 'Q') return false;}//檢查左上方有無for(int i = row - 1,j = col - 1; i >= 0 && j >= 0; i--, j--){if(chessboard[i][j] == 'Q') return false;}//檢查右上方有無for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++){if(chessboard[i][j] == 'Q') return false;}return true;}//n表示棋盤邊長,row表示當(dāng)前遍歷到第幾層了void backtracking(int n, int row, vector<string>& chessboard){//當(dāng)遞歸到棋盤最底層的時候,收集結(jié)果if(row == n){result.push_back(chessboard);return;}//單層邏輯,遞歸深度row控制行,每一層for循環(huán)col控制列for(int col = 0; col < n; col++){if(isValid(row,col,chessboard,n) == true){chessboard[row][col] = 'Q'; //放置皇后backtracking(n,row+1,chessboard);chessboard[row][col] = '.';}}return ;}vector<vector<string>> solveNQueens(int n) {result.clear();//注意一下棋盤的初始化方式vector<string> chessboard(n,string(n,'.'));backtracking(n,0,chessboard);return result;} };

37. 解數(shù)獨(dú)

https://leetcode-cn.com/problems/sudoku-solver/

class Solution { public:bool isValid(int row, int col, char val,vector<vector<char>>& board){//判斷同行是否重復(fù)for(int i = 0; i < 9; i++){if(board[row][i] == val) return false;}//判斷同列是否重復(fù)for(int i = 0; i < 9; i++){if(board[i][col] == val) return false;}//判斷9方格里是否重復(fù)int startRow = (row / 3) * 3;int startCol = (col / 3) * 3;for(int i = startRow; i < startRow + 3; i++){for(int j = startCol; j < startCol + 3; j++){if(board[i][j] == val) return false;}}//如果都沒錯return true;}bool backtracking(vector<vector<char>>& board){for(int i = 0; i < board.size(); i++){for(int j = 0; j < board[0].size(); j++){//如果已經(jīng)填入,continueif(board[i][j] != '.') continue;for(char k = '1'; k <= '9'; k++) //觀察(i,j)這個位置放置k是否合適{if(isValid(i,j,k,board)){board[i][j] = k; //放置if(backtracking(board)) return true; //如果找到一組合適的解,立刻返回board[i][j] = '.'; //回溯,撤銷k}}return false; //9個數(shù)都試完了,都不行,返回false;}}return true; //遍歷完沒有返回false,說明找到了合適的棋盤}void solveSudoku(vector<vector<char>>& board) {backtracking(board);} };

組合問題

77. 組合

未剪枝優(yōu)化

https://leetcode-cn.com/problems/combinations/
邏輯樹同一層:從左向右取數(shù),取過的數(shù)不再重復(fù)取
n相當(dāng)于樹寬,k相當(dāng)于樹深度。
每次搜索到葉子節(jié)點,就找到了一個結(jié)果。

class Solution { private: vector<vector<int>> result; //用來存放結(jié)果集合 vector<int> path; //用來存放結(jié)果 public:void backtracking(int n, int k, int startIndex){if(path.size() == k){result.push_back(path);return;}//單層邏輯for(int i = startIndex; i <= n; i++) //控制樹的橫向遍歷{path.push_back(i);backtracking(n,k,i+1); //下一層搜索從i+1開始path.pop_back(); //回溯} }vector<vector<int>> combine(int n, int k) {result.clear();path.clear();backtracking(n,k,1);return result;} };

剪枝優(yōu)化

單層邏輯中
如果for循環(huán)選擇的起始位置之后的元素個數(shù)已經(jīng)不足,就沒有必要再次搜索。
已經(jīng)選擇path.size(),還需要k - path.size(),所以可以這樣寫:

for(int i = startIndex; i <= n -(k - path.size()) + 1; i++)

216. 組合總和 III

https://leetcode-cn.com/problems/combination-sum-iii/

未剪枝優(yōu)化

class Solution { public:vector<vector<int>> result;vector<int> path;// 目標(biāo)和 樹深度 當(dāng)前path路徑和 本層起始點 void backtracking(int targetSum, int k, int sum, int startIndex){if(path.size() == k) //到達(dá)樹底部{if(sum == targetSum) result.push_back(path);return;}for(int i = startIndex; i <= 9; i++){sum += i;path.push_back(i);backtracking(targetSum,k,sum,i+1);path.pop_back();sum -= i;}}vector<vector<int>> combinationSum3(int k, int n) {result.clear();path.clear();backtracking(n,k,0,1);return result;} };

剪枝優(yōu)化

如果sum > targetSum ,那么往后遍歷就沒有意義了。于是在backtracking函數(shù)一開始加上:

if(sum > targetSum) return;

17. 電話號碼的字母組合

https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
細(xì)節(jié):
數(shù)字與字母的映射.與前兩題不同的是,本題是求不同集合之間的組合,前兩題是求同一個集合中的組合。

class Solution { private:const string letterMap[10] = {"", //0"", //1"abc", //2"def", //3"ghi", //4"jkl", //5"mno", //6"pqrs", //7"tuv", //8"wxyz", //9 }; public:vector<string> result;string s;void backtracking(const string& digits, int index){if(index == digits.size()){result.push_back(s);return;}int digit = digits[index] - '0';string letters = letterMap[digit];for(int i = 0; i < letters.size(); i++){s.push_back(letters[i]);backtracking(digits,index + 1); //下一層處理下一個數(shù)s.pop_back();}}vector<string> letterCombinations(string digits) {s.clear();result.clear();if(digits.size() == 0) return result;backtracking(digits,0);return result;} };

39. 組合總和

https://leetcode-cn.com/problems/combination-sum/
需要注意的點:
組合沒有數(shù)量要求,元素可以無限重復(fù)選取。

未剪枝優(yōu)化

class Solution { public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex){if(sum > target) return;if(sum == target) {result.push_back(path);return;}for(int i = startIndex; i < candidates.size(); i++){sum += candidates[i];path.push_back(candidates[i]);backtracking(candidates,target,sum,i);path.pop_back();sum -= candidates[i];}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {result.clear();path.clear();backtracking(candidates,target,0,0);return result;} };

剪枝優(yōu)化

對總集合先排序,如果本層sum + candidates[i] 已經(jīng)大于target,就沒有必要再向本層之后的元素遍歷了。

class Solution { public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex){if(sum > target) return;if(sum == target) {result.push_back(path);return;}for(int i = startIndex; i < candidates.size() && candidates[i] + sum <= target; i++){sum += candidates[i];path.push_back(candidates[i]);backtracking(candidates,target,sum,i);path.pop_back();sum -= candidates[i];}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {result.clear();path.clear();sort(candidates.begin(),candidates.end());backtracking(candidates,target,0,0);return result;} };

40. 組合總和 II,挺重要的,涉及到去重了

這一題和上一題有區(qū)別:
本題candidates中的每個數(shù)字再每個組合中只能用一次,并且candidates中有元素是重復(fù)的,并且解集中不能包含重復(fù)的組合。要在搜索的過程中就去掉重復(fù)組合。
去重:使用過的元素不能重復(fù)選取。注意組合問題的邏輯樹有兩個維度:深度和廣度
元素在同一個組合內(nèi)可以重復(fù),但是兩個組合不能相同,顯然是樹層廣度上的去重。
同時注意,對于樹層去重不需要使用輔助數(shù)組。

class Solution { public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex){if(sum > target) return;if(sum == target){result.push_back(path);return;}for(int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++){//去重if(i > startIndex && candidates[i] == candidates[i - 1]) continue;sum += candidates[i];path.push_back(candidates[i]);backtracking(candidates,target,sum,i+1);path.pop_back();sum -= candidates[i];}}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {result.clear();path.clear();//首先排序sort(candidates.begin(),candidates.end());backtracking(candidates,target,0,0);return result;} };

切割問題

切割問題類似于組合問題。

131. 分割回文串

https://leetcode-cn.com/problems/palindrome-partitioning/

class Solution { public:vector<vector<string>> result;vector<string> path;bool isHuiWen(const string& s,int start,int end){int i = start;int j = end;while(i < j){if(s[i] != s[j]) return false;i++;j--;}return true;}void backtracking(const string& s,int startIndex){if(startIndex >= s.size()){result.push_back(path);return;}for(int i = startIndex; i < s.size(); i++){//如果是回文子串if(isHuiWen(s,startIndex,i)){//獲取子串string str = s.substr(startIndex,i - startIndex + 1);path.push_back(str);backtracking(s,i+1);path.pop_back();}}}vector<vector<string>> partition(string s) {result.clear();path.clear();backtracking(s,0);return result;} };

子集問題

子集問題也是一種組合問題。不過不同的是組合問題是在到達(dá)葉子節(jié)點時候才將path送入result。而子集問題每次進(jìn)入這一層就要把path加入result,這才是子集。

78. 子集

https://leetcode-cn.com/problems/subsets/

class Solution { public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, int startIndex){//result.push_back(path);if(startIndex > nums.size()) return;for(int i = startIndex; i < nums.size(); i++){path.push_back(nums[i]);backtracking(nums,i+1);path.pop_back();}}vector<vector<int>> subsets(vector<int>& nums) {result.clear();path.clear();backtracking(nums,0);return result;} };

90. 子集 II,有樹層去重

https://leetcode-cn.com/problems/subsets-ii/
比之前加上一個樹層去重

class Solution { public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, int startIndex){//result.push_back(path);if(startIndex > nums.size()) return;for(int i = startIndex; i < nums.size(); i++){//樹層去重if(i > startIndex && nums[i-1] == nums[i]) continue;path.push_back(nums[i]);backtracking(nums,i+1);path.pop_back();}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {result.clear();path.clear();sort(nums.begin(),nums.end());backtracking(nums,0);return result;} };

491. 遞增子序列,也需要數(shù)層去重,使用unordered_set

https://leetcode-cn.com/problems/increasing-subsequences/
去重邏輯:
1、使用unordered_set用來記錄本層元素是否重復(fù),重復(fù)則continue
2、觀察待插入的元素nums[i]是否是比path尾部元素大等的,否則構(gòu)成不了遞增序列。

class Solution { public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, int startIndex){//子序列長度>=2if(path.size() > 1)result.push_back(path);if(startIndex > nums.size()) return;unordered_set<int> uset;for(int i = startIndex; i < nums.size(); i++){//樹層去重if(!path.empty() && nums[i] < path.back()) continue;if(uset.find(nums[i]) != uset.end()) continue;uset.insert(nums[i]);path.push_back(nums[i]);backtracking(nums,i+1);path.pop_back();}}vector<vector<int>> findSubsequences(vector<int>& nums) {result.clear();path.clear();backtracking(nums,0);return result;} };

排列問題

46. 全排列

https://leetcode-cn.com/problems/permutations/
與組合的區(qū)別在于for循環(huán)從0開始,但是需要注意取過的元素不能再取,這里使用used數(shù)組進(jìn)行記錄。

class Solution { public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, vector<bool>& used){if(path.size() == nums.size()) {result.push_back(path);return;}for(int i = 0; i < nums.size(); i++){//用過的元素不需要使用if(used[i] == true) continue;used[i] = true;path.push_back(nums[i]);backtracking(nums,used);path.pop_back();used[i] = false;}}vector<vector<int>> permute(vector<int>& nums) {result.clear();path.clear();vector<bool> used(nums.size(),false);backtracking(nums,used);return result;} };

47. 全排列 II + 數(shù)層去重

https://leetcode-cn.com/problems/permutations-ii/

class Solution { public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, vector<bool>& used){if(path.size() == nums.size()) {result.push_back(path);return;}for(int i = 0; i < nums.size(); i++){//去重,同一層nums[i-1]使用過的話直接跳過if(i > 0 && nums[i] == nums[i-1] && used[i-1] == true) continue;//用過的元素不需要使用if(used[i] == true) continue;used[i] = true;path.push_back(nums[i]);backtracking(nums,used);path.pop_back();used[i] = false;}}vector<vector<int>> permuteUnique(vector<int>& nums) {result.clear();path.clear();sort(nums.begin(),nums.end());vector<bool> used(nums.size(),false);backtracking(nums,used);return result;} };

行程安排+高級容器的使用,現(xiàn)在還沒吃透

https://leetcode-cn.com/problems/reconstruct-itinerary/
還得再多看看。

class Solution { public://unordered_map<出發(fā)機(jī)場,map<到達(dá)機(jī)場,航班次數(shù)>> targets;unordered_map<string,map<string,int>> targets;//還有多少航班 tickets//由于只需要找到一個行程,所以找到直接返回,使用bool正好bool backtracking(int ticketNum,vector<string>& result){if(result.size() == ticketNum + 1) return true;for(pair<const string,int>& target : targets[result[result.size() - 1]]){if(target.second > 0) {result.push_back(target.first);target.second--;if(backtracking(ticketNum,result)) return true;result.pop_back();target.second++;}}return false;}vector<string> findItinerary(vector<vector<string>>& tickets) {targets.clear();vector<string> result;//初始化targetsfor(const vector<string>& vec : tickets){targets[vec[0]][vec[1]]++; //記錄映射關(guān)系}result.push_back("JFK"); //起始機(jī)場backtracking(tickets.size(),result);return result;} };

總結(jié)

以上是生活随笔為你收集整理的算法题复习(回溯)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。