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

暴力穷举,这是不可避免,因为回溯法没有重复子结构,所以其时间复杂度大于等于O(N!);


前言

本文章内容部分参考公众号labuladong关于回溯算法的讲解,仅为笔者日后复习,或读者参考学习,无商业用途。

实质:决策树的遍历

三要素

  1. 路径:当前已经做出的决策;
  2. 选择列表:当前可以做出的决策;
  3. 结束条件:到达决策树底层,一般就是选择列表为空;

回溯递归框架

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}进行全排列:

  1. 保持2不变,对3、4全排列;
  2. 保持3不变,对4进行全排列;
  3. 得出2, 3, 4;
  4. 交换3、4;
  5. 保持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皇后问题



大体意思:

思路
  1. 套用框架,明确三要素路径、选择列表和结束条件;
  2. 当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皇后问题)相关推荐

  1. python 动态规划 回溯_回溯算法 - 全排列算法实现(pythondart)

    回溯算法 , 就是 穷举 解决一个回溯问题,实际上就是一个决策树的遍历过程. 路径: 也就是已经做出的选择 选择列表: 也就是你当前可以做的选择 结束条件: 也就是到达决策树底层,无法再做选择的条件. ...

  2. python回溯算法全排列_python 回溯法 子集树模板 系列 —— 11、全排列

    问题 实现 'a', 'b', 'c', 'd' 四个元素的全排列. 分析 这个问题可以直接套用排列树模板. 不过本文使用子集树模板.分析如下: 一个解x就是n个元素的一种排列,显然,解x的长度是固定 ...

  3. 【Leetcode 剑指offer刷题】回溯算法-排列组合77216

    目录 leetcode.77.组合 leetcode.216.组合总和 III 两题差异 77题代码 216题代码 参考 leetcode.77.组合 题目链接 给定两个整数 n 和 k,返回 1 - ...

  4. 回溯算法-排列/组合/子集

    文章目录 子集 https://mp.weixin.qq.com/s?subscene=19&__biz=MzAxODQxMDM0Mw==&mid=2247485007&idx ...

  5. 本题要求实现一个求整数的逆序数的简单函数。_回溯算法:求组合总和(二)...

    给「代码随想录」一个星标吧! ❝ 我将公众号文章和学习相关的资料整理到了Github :https://github.com/youngyangyang04/leetcode-master,方便大家在 ...

  6. 算法实验二 【八皇后问题】(回溯算法)

    算法实验二 [八皇后问题](回溯算法) 1007.8皇后问题 时限:1000ms 内存限制:10000K 总时限:3000ms 描述 输出8皇后问题所有结果. 输入 没有输入. 输出 每个结果第一行是 ...

  7. python 回溯算法总结

    python 回溯算法 回溯算法理论基础 组合 组合总数III 电话号码的字母组合 组合总和 组合总和ii 分割回文串 复原IP地址 子集问题 子集问题II 递增序列 全排列 全排列II 重新安排行程 ...

  8. 「leetcode」最强回溯算法总结篇!历时21天、画了20张树形结构图、14道精选回溯题目精讲

    本文 https://github.com/youngyangyang04/leetcode-master 已经收录,里面还有leetcode刷题攻略.各个类型经典题目刷题顺序.思维导图,可以fork ...

  9. Nuist集训队作业:深度优先搜索(回溯算法)

    Nuist集训队第一次作业:深度优先搜索(回溯算法) 引例 深搜基本思想及回溯算法模板 P1706 全排列问题 P1219 八皇后 P1605 迷宫 P1101 单词方阵 小结 引例 国际西洋棋棋手马 ...

  10. 数字拆分问题算法回溯_回溯算法:求子集问题!

    给「代码随想录」一个星标吧! ❝ 认识本质之后,这就是一道模板题 通知:我将公众号文章和学习相关的资料整理到了Github :https://github.com/youngyangyang04/le ...

最新文章

  1. 面向对象基础知识01
  2. SAP CRM WebClient UI和Hybris CommerceUI tag的渲染逻辑
  3. mysql server 5.0安装教程_MySQL Server 5.0安装教程
  4. python的zip方法_python zip()函数使用方法解析
  5. 详细地图_一目了然:蒙城学区划分详细地图
  6. mysql 冗余字段_18.3.3 增加冗余字段
  7. 开源开放 | OpenKG 更新发布新冠概念、防控和流行病等多个知识图谱
  8. 光伏并网逆变器意大利CEI 0-21标准
  9. 通道抠图蒙版置入背景(抠图换背景)
  10. Access denied for user ‘root‘@‘localhost‘
  11. 根据行数要求实现展开与隐藏TextView
  12. java里SQL insert操作的语法_Java含个人总结语法:JDBC,学生表,实体类,集合,增删改查,注入,预处理【诗书画唱】...
  13. 微信第三方平台【六】微信开放平台帐号管理
  14. 【halcon】灰度直方图直观理解与应用
  15. 【LeetCode】345. Reverse Vowels of a String 解题报告
  16. win10重置进度条不动了_win10重置卡在100%不动没反应怎么办
  17. 数据库原理及MySQL应用 | 数据库安全加固
  18. 微型计算机的特点及其主板构成,第1章 计算机基础知识教案
  19. U盘加密软件怎么选?
  20. obsidian之快速拆分文件的插件note-refactor操作手册

热门文章

  1. 电脑自带的实用酷炫关机,你会吗?!
  2. 【2023年的就业形势依旧严峻】
  3. 最近连续三周活跃用户数
  4. WPS中如何嵌入电子签名?
  5. Maven打包 关于“There aretest failures”的错误解决
  6. php后缀名是什么意思,后缀名为php是什么意思
  7. linux 中dev目录,详解Linux系统下的/dev目录
  8. 计算机高水平竞赛,计算机学院学子在CCSP竞赛中荣获佳绩
  9. 【无标题】20个it资源学习网站
  10. 尚德自考的销售策略,尚德的陷阱,尚德是如何欺骗消费者的,315刚过,尚德就可以这样?