回溯算法(全排列、组合、N皇后问题)
回溯算法(全排列、N皇后問題)
暴力窮舉,這是不可避免,因為回溯法沒有重復子結構,所以其時間復雜度大于等于O(N!);
前言
本文章內容部分參考公眾號labuladong關于回溯算法的講解,僅為筆者日后復習,或讀者參考學習,無商業用途。
實質:決策樹的遍歷
三要素
回溯遞歸框架
result = [] def backtrack(路徑, 選擇列表):if 滿?結束條件:result.add(路徑)returnfor 選擇 in 選擇列表:做選擇backtrack(路徑, 選擇列表)撤銷選擇核心:for循環中的遞歸,遞歸前做出決策,遞歸后撤銷決策
經典例題
全排列
問題:n個非重復數字的全排列?
代碼實現
回溯框架
/* full permutation track: track path nums: selection list */ void permute(vector<int> track, vector<int> &nums){/* end condition */if(track.size() == nums.size()){result.push_back(track);/* result is the global vector */ }/* backtrack *//* layer one have nums.size() paths */for(auto n :nums){/* make sure the chioce valid */if(find(track, n)){continue;}/* make choice */track.push_back(n);/* to down recursion */permute(track, nums);/* undo choice */track.pop_back();} }判斷元素是否在選擇列表中;
 這樣判斷時間復雜度是O(n),當然我們可以通過set的方式記錄,用空間換時間,實際上,我們還可以使用交換的方式;
打印
/* vector二維數組打印 */ void printVec(vector<vector<int>> &obj){for(auto item : obj){for(auto it : item){cout << " " << it;}cout << endl;} }主函數
vector<vector<int>> result; int main(){vector<int> nums;nums.assign({2, 3, 5, 4});permute(vector<int>(), nums);printVec(result);getchar();return 0; }全排列經典算法(交換寫法)
思路
 對{2, 3, 4}進行全排列:
總結就是:
 每個數依次打頭,求剩下的n-1個數的全排列,而n-1個數的全排列由n-2個數推出…直到一個數的全排列,開始回溯;
組合
代碼實現:
class Solution {vector<vector<int>> res; public:void combine(vector<int> track, int pinNum, int n, int k){/* end condition */if(track.size() == k){res.push_back(track);return ;}/* begin num */for(int i = pinNum; i <= n; i++){track.push_back(i);combine(track, i+1, n, k);/* i+1而不是pinNum+1,因為自身不能選兩次 */track.pop_back();}}vector<vector<int>> combine(int n, int k) {combine(vector<int>(), 1, n, k);return res;} };N皇后問題
 
 大體意思:
 
思路
代碼實現
聲明全局數組為結果集
vector<vector<string>> res;主調函數:
vector<vector<string>> solveNQueen(int n){/* 初始化路徑track為'.',可選列表從track[0]開始 */backtrack(vector<string>(n, '.'), 0);return res; }套用框架:
 路徑:當前擺放Queen行之前成功擺放Queen的位置;
 選擇列表:當前行的所有位置都是選項,不過需要判斷是否合法;
 結束條件:Queen已經成功擺放完第N行;
檢查暴力當前位置是否合法
bool isValid(vector<string> track, int x, int y) {/* 當前列 */for (int i = 0; i < x; i++) {if (track[i][y] == 'Q') {return false;}}/* 左上 */for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {if (track[i][j] == 'Q') {return false;}}/* 右上 */for (int i = x - 1, j = y + 1; i >= 0 && j < track[i].size(); i--, j++) {if (track[i][j] == 'Q') {return false;}}return true; }哈希表檢查是否合法,需要聲明一個全局哈希表memo,并在每次做出選擇時memo[row] = col,每次撤銷選擇,刪除該鍵值對;
bool isValid(int row, int col) {for (auto p : memo) {if (p.second == col || abs(p.first - row) == abs(p.second - col)) {return false;}}return true; }總結
回溯算法的實質還是多叉樹的遍歷問題,深度優先遍歷,清楚遍歷的操作,框架如下:
def backtrack(...)for 選擇 in 選擇列表make choicebacktrack(...)undo choice注意
 維護好走過的 [路徑] 和當前可選的 [選擇列表],并寫出合適判斷選擇合法性的函數,最后碰到終止條件,將本次遍歷結果存入結果集;
2020/08/06 00:45
 @luxurylu
總結
以上是生活随笔為你收集整理的回溯算法(全排列、组合、N皇后问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: nrf51822之间通讯
- 下一篇: window下安装wamp环境
