【蓝桥杯速成】| 12.回溯排列&N皇后

题目一:全排列

问题描述

46. 全排列 - 力扣(LeetCode)

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

复制代码
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

复制代码
输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

复制代码
输入:nums = [1]
输出:[[1]]

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

解题步骤

这个题目属于排列问题,那么和我们之前做过的组合问题的区别就在于次序有别

1,2,3\],\[1,3,2\],\[2,1,3\],\[2,3,1\],\[3,1,2\],\[3,2,1\]在组合问题中是要被去重的 而在排列问题中则正是我们所要的结果 所以这一题的关键在于抛弃之前的组合去重逻辑,使用startindex缩减范围 改为只要求使用过的数不重复即可 只去重纵向的重复我们可以用used数组来实现 只要给每一个数字打上用过or没用过的标记 我们在循环中就能避免\[1,1,1\],\[1,1,2\]这样的答案 回溯三部曲 #### **1.确定参数及返回值** 依旧利用两个全局变量path,result分别存放过程结果和最终结果集 所以该函数无需返回值 根据上面的分析参数就应该为nums数组及used数组 > void backtracking(vector\\& nums,vector\\& used) #### **2.确定终止条件** 排列问题的结果还是在叶子结点取到,并且这个答案的长度和原数组长度一致 总不能你让小朋友去排队结果弄丢一个吧 所以当path与nums数组长度一致时,把path加入result > if(path.size()==nums.size()){ > > result.push_back(path); > > } #### **3.单层遍历逻辑** 由于我们这里不再使用startindex,每次遍历就应该从0开始, 然后借助used数组排除用过的元素 再把合适的元素加入path,并修改它的使用标志即used数组对应值 递归调用backtracking函数,再对path,used做回溯 > for(int i=0;i\ > if(used\[i\]==true){ > > continue; > > } > > path.push_back(nums\[i\]); > > used\[i\]==true; > > backtracking(nums,used); > > used\[i\]=false; > > path.pop_back(); > > } 最后别忘记在主函数中定义used数组并全部初始化为false > vector\ used(nums.size(),false); 整合后的完整代码如下! ### code ```cpp class Solution { public: vector path; vector> result; void backtracking(vector& nums,vector& used){ if(path.size()==nums.size()){ result.push_back(path); return; } for(int i=0;i> permute(vector& nums) { vector used(nums.size(),false); backtracking(nums,used); return result; } }; ``` *** ** * ** *** ## 题目二:全排列② ### 问题描述 [47. 全排列 II - 力扣(LeetCode)](https://leetcode.cn/problems/permutations-ii/ "47. 全排列 II - 力扣(LeetCode)") 给定一个可包含重复数字的序列 `nums` ,***按任意顺序*** 返回所有不重复的全排列。 **示例 1:** ``` 输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]] ``` **示例 2:** ``` 输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] ``` **提示:** * `1 <= nums.length <= 8` * `-10 <= nums[i] <= 10` ### 解题步骤 这一题是上一题的升级版,区别是nums中可能有重复元素 那么我们需要多做一个树层去重 这个去重逻辑之前也用过 就是要先对nums数组做个排序,方便确认选择数字是否之前就存在 同时在树层间确认used\[i-1\]==false 即前一个元素是否在这一层被使用过,false是因为用完回溯了 > if(i\>0 \&\& nums\[i\]==nums\[i-1\] \&\& used\[i-1\]==false){ > > continue; > > } 树层重复解决完也不能忘记纵向间也要保持去重逻辑 一个数字不能取两遍,还是需要used数组实现 > if(used\[i\]!=true){ > > path.push_back(nums\[i\]); > > used\[i\]=true; > > backtracking(nums,used); > > used\[i\]=false; > > path.pop_back(); > > } 其它保持不变,完整代码在下方! ### code ```cpp class Solution { public: vector path; vector> result; void backtracking(vector& nums,vector& used){ if(path.size()==nums.size()){ result.push_back(path); return; } for(int i=0;i0 && nums[i]==nums[i-1] && used[i-1]==false){ continue; } if(used[i]!=true){ path.push_back(nums[i]); used[i]=true; backtracking(nums,used); used[i]=false; path.pop_back(); } } } vector> permuteUnique(vector& nums) { vector used(nums.size(),false); sort(nums.begin(),nums.end()); backtracking(nums,used); return result; } }; ``` *** ** * ** *** ## 题目三:N皇后 ### 问题描述 [51. N 皇后 - 力扣(LeetCode)](https://leetcode.cn/problems/n-queens/description/ "51. N 皇后 - 力扣(LeetCode)") 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 **n 皇后问题** 研究的是如何将 `n` 个皇后放置在 `n×n` 的棋盘上,并且使皇后彼此之间不能相互攻击。 给你一个整数 `n` ,返回所有不同的 **n皇后问题** 的解决方案。 每一种解法包含一个不同的 **n 皇后问题** 的棋子放置方案,该方案中 `'Q'` 和 `'.'` 分别代表了皇后和空位。 ![](https://i-blog.csdnimg.cn/direct/8da351b0f0e24f0693b8f45875f3810a.png) ### 解题步骤 这一题与之前所有回溯算法的区别在于它是一个二维的 但实际上我们的解题思路还是类似 结果改为用vector\\> result存储(实际上相当于3维,string也算一个维度) 每一个答案是这个棋盘的皇后摆放情况,那么这个就是我们的过程量 原来用path存放,此处为了方便解题我们可以设置为vector\ chessboard 这里需要注意不能初始化为 `vector chessboard(n,".");` 这是创建一个包含`n`个元素的`vector`,每个元素是一个`string`。 每个`string`被初始化为`"."`(即长度为1的字符串,内容是`'.'`)。 因此,`chessboard`是一个`n`行的棋盘,但每行只有1个字符`'.'`。 例如,`n=4`时: chessboard = { ".", ".", ".", "." }; 这与我们想要的 chessboard = { "....", "....", "....", "...." }; 完全不符,所以需要定义棋盘并将其初始化为: > std::vector\ chessboard(n,std::string(n,'.')); 当然也可以定义为vector\\>,该版本代码附在最后 下面就是使用回溯三部曲,写出主要代码 #### 1.明确参数及返回值 result数组被定义为全部变量,故依旧无需返回值 chessboard作为我们要操作的过程量肯定是参数的一员 同时n作为棋盘大小是我们的边界 那么还需要一个索引指向处理的层数,故使用 int row 这样既可以通过层数了解,定位处理的位置 也可以作为终止条件的参考 > void backtracking(vector\\>\& chessboard,int n,int row) #### **2.确定终止条件** 我们需要填好整个棋盘才算做一个答案,故结果在叶子结点处取到 也就是row指向最后一层n > if(row==n){ > > result.push_back(chessboard); > > return; > > } #### **3.单层遍历操作** 在这个部分我们要遍历当前行所有格子,把合法的皇后放进棋盘, 并递归调用函数填满整个棋盘 每次调用后需要回溯 > for(int col=0;col\ > if(isValid....){//检查函数等会再写! > > chessboard\[row\]\[col\]='Q'; > > backtracking(chessboard,n,row+1); > > chessboard\[row\]\[col\]='.'; > > } > > } #### **4.判断合法性函数** 按照规则,我们要确保放入皇后的位置正确, 那么需要用到的参数肯定有当前位置的row和col,棋盘内容chessboard,棋盘大小n 返回值应该是bool型的 那么函数返回值和参数列表应该为 > bool isValid(vector\\& chessboard,int n,int row,int col) 在函数中我们需要进行三个检查 1)列间检查 确保每一列只有一个皇后, 那么需要遍历当前行之前的所有行,去查看\[col\]这个位置是否有Q > for(int i=0;i\ > if(chessboard\[i\]\[col\]=='Q'){ > > return false; > > } > > } 2)45度角检查 确保左上斜线处不存在Q 映照棋盘关系,左上处坐标就是当前格横坐标减一(row-1),纵坐标减一(col-1) 需要一直往左上查找所以不断减减,直到边界 > for(int i=row-1, j=col-1; i\>=0\&\&j\>=0; i--, j--){ > > if(chessboard\[i\]\[j\]=='Q') > > return false; > > } 3)135度角检查 原理和上面的类似,不过135度指右上斜线 > for(int i = row - 1, j = col + 1; i \>= 0 \&\& j \< n; i--, j++) {//右边上面一个! > > if (chessboard\[i\]\[j\] == 'Q') { > > return false; > > } > > } 都没有违反规则则返回true 然后需要在backtracking函数中正确传参调用即可 完整代码如下! ### code **vector\版** ```cpp class Solution { public: vector> result; bool isValid( vector& chessboard, int n,int row, int col) { // 检查列 for (int i = 0; i < row; i++) { //该列在当前行之前是否出现过Q if (chessboard[i][col] == 'Q') { return false; } } // 检查 45度角是否有皇后 for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {//就是左边上面一个! if (chessboard[i][j] == 'Q') { return false; } } // 检查 135度角是否有皇后 for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {//右边上面一个! if (chessboard[i][j] == 'Q') { return false; } } return true; } void backtracking(vector& chessboard,int n,int row){ if(row==n){ result.push_back(chessboard); return; } for(int col=0;col> solveNQueens(int n) { //vector chessboard(n,"."); std::vector chessboard(n, std::string(n, '.')); backtracking(chessboard,n,0); return result; } }; ``` **vector\\> chessboard版** ```cpp class Solution { public: vector> result; bool isValid(vector>& chessboard, int n, int row, int col) { 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; } void backtracking(vector>& chessboard, int n, int row) { if (row == n) { vector board; for (const auto& row : chessboard) { board.push_back(string(row.begin(), row.end())); } result.push_back(board); return; } for (int col = 0; col < n; col++) { if (isValid(chessboard, n, row, col)) { chessboard[row][col] = 'Q'; backtracking(chessboard, n, row + 1); chessboard[row][col] = '.'; } } } vector> solveNQueens(int n) { vector> chessboard(n, vector(n, '.')); backtracking(chessboard, n, 0); return result; } }; ```

相关推荐
照海19Gin7 分钟前
数据结构之约瑟夫环的问题
c语言·数据结构·算法
Joe_Wang58 分钟前
[leetcode]1749. 任意子数组和的绝对值的最大值(dp)
算法·leetcode
烟锁池塘柳020 分钟前
【数学建模】(启发式算法)遗传算法:自然选择的计算模型
算法·数学建模·启发式算法
Min_小明27 分钟前
CMake 简单使用总结
android·开发语言·算法
森焱森1 小时前
格雷码、汉明码,CRC校验的区别
服务器·c语言·网络·人工智能·算法
三歪爱三玖1 小时前
【蓝桥杯】单片机设计与开发,PWM
单片机·职场和发展·蓝桥杯
YaoSolar1 小时前
刷题记录(LeetCode 994.腐烂的橘子)
算法·leetcode·宽度优先
mazo_command1 小时前
LeetCode热题100|128.最长连续序列,283.移动零
leetcode·哈希算法·散列表
米芝鱼1 小时前
LearnOpenGL(九)自定义转换类
开发语言·c++·算法·游戏·图形渲染·shader·opengl
Rsecret21 小时前
个人学习编程(3-29) leetcode刷题
学习·算法·leetcode