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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

回溯算法(全排列、组合、N皇后问题)

發布時間:2024/3/24 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 回溯算法(全排列、组合、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的方式記錄,用空間換時間,實際上,我們還可以使用交換的方式;

    /* make sure the chioce valid */ bool find(vector<int> track, int n){for(auto t : track){if(t == n){return true;}}return false; }

    打印

    /* 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}進行全排列:

  • 保持2不變,對3、4全排列;
  • 保持3不變,對4進行全排列;
  • 得出2, 3, 4;
  • 交換3、4;
  • 保持4不變,對3進行全排列;
  • 總結就是:
    每個數依次打頭,求剩下的n-1個數的全排列,而n-1個數的全排列由n-2個數推出…直到一個數的全排列,開始回溯;

    /* nums:個位數的數組(假設從 1-9),eg:{2, 3, 4}; obj: 記錄全排列結果; pinNum:pinNum+1表示當前固定的數字個數,從1開始; len:總個位數,res的長度; */ void permutation(vector<int> &nums, vector<vector<int>> &res, int pinNum, int len){/* 判斷是否到達最后一個數 */if(pinNum+1 == len){/* 將當前排列記錄到obj */res.push_back(nums);return;}/* pinNum's full permutation */for(int i=pinNum; i<len; i++){swap(nums[i], nums[pinNum]);permutation(res, obj, pinNum+1, len);swap(nums[i], nums[pinNum]);} }

    組合

    代碼實現:
    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皇后問題



    大體意思:

    思路
  • 套用框架,明確三要素路徑、選擇列表和結束條件;
  • 當Queen成功擺放完第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行;

    void backtrack(vector<string> &track, int row){int n = track.size();/* end condition */if(row == n){res.push_back(track);return;}for(int col = 0; col < n; col++){/* 檢查當前位置是否合法 */if(!isValid(track, row, col){continue;}/* make choice */track[row][col] = 'Q';/* recursion */backtrack(track, row+1);/* undo choice */track[row][col] = '.';} }

    檢查暴力當前位置是否合法

    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皇后问题)的全部內容,希望文章能夠幫你解決所遇到的問題。

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