day30| 332.重新安排行程、51. N皇后、37. 解数独

目录:

解题及思路学习

332. 重新安排行程

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

示例 1:

!https://assets.leetcode.com/uploads/2021/03/14/itinerary1-graph.jpg

输入:tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
输出:["JFK","MUC","LHR","SFO","SJC"]

思考:之前华为有道笔试题就考的类似。有点像深搜,使用了回溯。

这道题目有几个难点:

  1. 一个行程中,如果航班处理不好容易变成一个圈,成为死循环
  2. 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
  3. 使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?
  4. 搜索的过程中,如何遍历一个机场所对应的所有机场。

记录映射关系:

一个机场映射多个机场,机场之间要靠字母序排列,一个机场映射多个机场,可以使用std::unordered_map,如果让多个机场之间再有顺序的话,就是用std::map 或者std::multimap 或者 std::multiset。

unordered_map<string, map<string, int>> tartgets; unordered_map<出发机场, map<达到机场,航班次数>> targets.

在遍历 unordered_map<出发机场, map<到达机场, 航班次数>> targets的过程中,可以使用"航班次数"这个字段的数字做相应的增减,来标记到达机场是否使用过了。

如果“航班次数”大于零,说明目的地还可以飞,如果“航班次数”等于零说明目的地不能飞了,而不用对集合做删除元素或者增加元素的操作。

相当于说我不删,我就做一个标记!

for (pair<const string, int>& target : targets[result[result.size() - 1]]) 这行代码是因为同一个出发点可以有多个降落点,所以在最新的出发点,寻找满足条件的节点。

class Solution {
public:// unordered_map<出发机场, map<到达机场, 航班次数>> targetsunordered_map<string, map<string, int>> targets;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;for (const vector<string>& vec : tickets) {targets[vec[0]][vec[1]]++;   // 记录映射关系}result.push_back("JFK");  // 起始机场backtracking(tickets.size(), result);return result;}
};

本题难在容器的选择和使用上

本题其实是一道深度优先搜索的题目,但是我完全使用回溯法的思路来讲解这道题题目,算是给大家拓展一下思维方式,其实深搜和回溯也是分不开的,毕竟最终都是用递归

51. N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 **n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

!https://assets.leetcode.com/uploads/2020/11/13/queens.jpg

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

思考:这道题用回溯搜索。 另写一个算法判断放在当前位置是否满足要求。

皇后们的约束条件:

  1. 不能同行
  2. 不能同列
  3. 不能同斜线
class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到棋盘的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {if (row == n) {result.push_back(chessboard);return;}for (int col = 0; col < n; col++) {if (isValid(row, col, chessboard, n)) { // 验证合法就可以放chessboard[row][col] = 'Q'; // 放置皇后backtracking(n, row + 1, chessboard);chessboard[row][col] = '.'; // 回溯,撤销皇后}}
}
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;}}// 检查 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;
}
public:vector<vector<string>> solveNQueens(int n) {result.clear();std::vector<std::string> chessboard(n, std::string(n, '.'));backtracking(n, 0, chessboard);return result;}
};
  • 时间复杂度: O(n!)
  • 空间复杂度: O(n)

除了验证棋盘的合理性,其他都是按照模板来的。

棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度,这样就可以套进回溯法的模板里了

37. 解数独

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

!https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2021/04/12/250px-sudoku-by-l2g-20050714svg.png

输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]

思考:只需要传入整个棋盘就行。然后用i,j表示行列,分别进行判断。

class Solution {
private:
bool backtracking(vector<vector<char>>& board) {for (int i = 0; i < board.size(); i++) {        // 遍历行for (int j = 0; j < board[0].size(); j++) { // 遍历列if (board[i][j] == '.') {for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适if (isValid(i, j, k, board)) {board[i][j] = k;                // 放置kif (backtracking(board)) return true; // 如果找到合适一组立刻返回board[i][j] = '.';              // 回溯,撤销k}}return false;  // 9个数都试完了,都不行,那么就返回false}}}return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}
bool isValid(int row, int col, char val, vector<vector<char>>& board) {for (int i = 0; i < 9; i++) { // 判断行里是否重复if (board[row][i] == val) {return false;}}for (int j = 0; j < 9; j++) { // 判断列里是否重复if (board[j][col] == val) {return false;}}int startRow = (row / 3) * 3;int startCol = (col / 3) * 3;for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复for (int j = startCol; j < startCol + 3; j++) {if (board[i][j] == val ) {return false;}}}return true;
}
public:void solveSudoku(vector<vector<char>>& board) {backtracking(board);}
};

感觉跟N皇后问题很类似,都是传入棋盘,然后遍历上面的位置,判断放置是否合适。再需要单独写一个判断函数。

复盘总结

个人反思

1、回溯的模板

void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果}
}

2、组合问题。用递归控制for循环嵌套的数量!for循环横向遍历,递归纵向遍历,回溯不断调整结果集。

3、如果是一个集合来求组合的话,就需要startIndex。如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex。

4、去重的逻辑中,可以使用used数据也可以使用startindex进行去重。要先排序。

5、子集问题。在树形结构中子集问题是要收集所有节点的结果,而组合问题是收集叶子节点的结果

6、排列问题。处理排列问题就不用使用startIndex了。

  • 每层都是从0开始搜索而不是startIndex
  • 需要used数组记录path里都放了哪些元素了

7、使用set去重的版本相对于used数组的版本效率都要低很多。使用used数组在时间复杂度上几乎没有额外负担!

8、N皇后,只要搜索到了树的叶子节点,说明就找到了皇后们的合理位置了棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度,这样就可以套进回溯法的模板里了

9、解数独。本题中棋盘的每一个位置都要放一个数字,并检查数字是否合法,解数独的树形结构要比N皇后更宽更深

复杂度:

子集问题分析:

  • 时间复杂度:O(2n),因为每一个元素的状态无外乎取与不取,所以时间复杂度为O(2n)
  • 空间复杂度:O(n),递归深度为n,所以系统栈所用空间为O(n),每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为O(n)

排列问题分析:

  • 时间复杂度:O(n!),这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * … 1 = n!。
  • 空间复杂度:O(n),和子集问题同理。

组合问题分析:

  • 时间复杂度:O(2^n),组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
  • 空间复杂度:O(n),和子集问题同理。

N皇后问题分析:

  • 时间复杂度:O(n!) ,其实如果看树形图的话,直觉上是O(n^n),但皇后之间不能见面所以在搜索的过程中是有剪枝的,最差也就是O(n!),n!表示n * (n-1) * … * 1。
  • 空间复杂度:O(n),和子集问题同理。

解数独问题分析:

  • 时间复杂度:O(9^m) , m是’.'的数目。
  • 空间复杂度:O(n2),递归的深度是n2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/53629.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis与Spring的集成整合加优化分页功能

目录 一.为什么要将MyBatis和Spring整合&#xff1f;&#xff1f;&#xff1f; 二.配置环境 2.1 pom文件 2.2 xml文件 三.演示举例 四.Aop整合pageHelper 分页插件 今天的分享就到这啦&#xff01;&#xff01;&#xff01; 一.为什么要将MyBatis和Spring整合&#xff1f…

arm: day8

1.中断实验&#xff1a;按键控制led灯 流程&#xff1a; key.h /*************************************************************************> File Name: include/key.h> Created Time: 2023年08月21日 星期一 17时03分20秒***************************************…

Python项目开发案例————学生信息管理系统(附源码)

一、学生信息管理系统 本文使用Python语言开发了一个学生信息管理系统&#xff0c;该系统可以帮助教师快速录入学生的信息&#xff0c;并且对学生的信息进行基本的增、删、改、查操作&#xff1b;还可以实时地将学生的信息保存到磁盘文件中。 1.1 需求分析 为了顺应互联网时代…

(1)进程与线程区别

1.什么是线程、进程 进程&#xff1a;操作系统资源分配的基本单位线程&#xff1a;处理器任务调度和执行的基本单位。 一个进程至少有一个线程&#xff0c;线程是进程的一部分&#xff0c;所以线程也被称为轻权进程或者轻量级进程。 2.并行与并发 一个基本的事实前提&#x…

图神经网络与分子表征:番外——基组选择

学过高斯软件的人都知道&#xff0c;我们在撰写输入文件 gjf 时需要准备输入【泛函】和【基组】这两个关键词。 【泛函】敲定计算方法&#xff0c;【基组】则类似格点积分中的密度&#xff0c;与计算精度密切相关。 部分研究人员借用高斯中的一系列基组去包装输入几何信息&am…

图论(基础)

知识&#xff1a; 顶点&#xff0c;边 | 权&#xff0c;度数 1.图的种类&#xff1a; 有向图 | 无向图 有环 | 无环 联通性 基础1&#xff1a;图的存储&#xff08;主要是邻接矩阵和邻接表&#xff09; 例一&#xff1a;B3643 图的存储 - 洛谷 | 计算机科学教育新生态 (…

如何实现24/7客户服务自动化?建设智能客服知识库

客户自助服务是指用户通过企业或者第三方建立的网络平台或者终端&#xff0c;实现相关的自定义处理。实现客户服务自动化&#xff0c;对提高客户满意度、维持客户关系至关重要。客户服务自动化可以帮助企业以更快的速度和更高的效率来满足客户的售后服务要求&#xff0c;以进一…

C++的引用和函数重载

左值的引用(reference) 如果想要实现两个数据的交换&#xff0c;进行值传递时&#xff0c;不能交换实参&#xff0c;使用地址传递会交换实参(额外开辟空间) 概念 引用其实就是给变量起了一个别名 C是对C的一个重要的扩充 定义 数据类型 &引用名 目标名 引用的注意事项…

分享两道Java面试的算法上机题目(后续会持续补充更多)

所有题目参考答案均是小编自己想法&#xff0c;仅供参考&#xff0c;解法很多&#xff0c;大可不必局限&#xff0c;有更优解的大神无解&#xff0c;可评论或私聊博主指正&#xff01; 题目1 找大串&#xff0c;给定一个字符串其中包含任意组连续字符&#xff0c;我们把超过3个…

常见的移动端布局

流式布局&#xff08;百分比布局&#xff09; 使用百分比、相对单位&#xff08;如 em、rem&#xff09;等来设置元素的宽度&#xff0c;使页面元素根据视口大小的变化进行调整。这种方法可以实现基本的自适应效果&#xff0c;但可能在不同设备上显示不一致。 <!DOCTYPE ht…

Enable Secure boot on software

User Guide&#xff1a; Secure Boot V1Secure Boot V2espsecure.py Please follow these steps: 1、Query the chip version esptool.py chip_idThe V1.0 version chip only supports Secure boot V1 The V3.0 or later version chip support Secure boot V2 2、You need…

如何把一段str字符转换成字典?

可以使用Python内置的eval()函数将字符串转换为字典。不过需要注意使用eval()函数时需要保证输入的字符串格式正确&#xff0c;否则可能会出现异常&#xff0c;甚至安全隐患。以下是一个使用eval()函数将字符串转换为字典的例子&#xff1a; s "{key1: value1, key2: va…

find命令的用法

文章目录 [TOC](文章目录) 前言一、根据文件名来搜索(-name)二、根据文件类型来搜索 (-type)三、根据文件大小来搜索 (-size)四、根据目录层级来搜索五、同时执行多步操作六、总结 前言 find命令主要功能是根据文件的属性, 如&#xff1a;文件名, 文件类型, 文件大小, 文件的目…

成集云 | 畅捷通T+cloud连接器自动同步财务费用单至畅捷通 | 解决方案

源系统成集云目标系统 方案介绍 财务管理作为企业管理中重要的组成部分&#xff0c;在企业的发展和成长中扮演着重要角色&#xff0c;成集云以钉钉费用单OA审批与畅捷通TCloud系统为例&#xff0c;与钉钉连接器深度融合&#xff0c;通过数据处理和字段匹配实现了费用…

1558 Euro Efficiency (ZOJ)

看了下别人的写法&#xff0c;跟我写的都不一样……也不知道我的做法有没有疏漏&#xff0c;但可以AC。 #include <cstdio> #include <set> #include <algorithm> const int maxN 101; const int INF 99999999;int n, t, maxx, sum; std::set<int> …

【C51 GPIO的原理和内部结构】

51单片机项目基础篇 中篇&#xff1a;介绍GPIO1、认识GPIO2、GPIO 结构框图与工作原理2.1、P0端口结构框图与工作原理2.1.1、剖析组成 P0 口的每个单元的作用2.1.2、 P0 口做为 I/O 口及地址/数据总线使用时的具体工作过程 2.2、P1 端口结构框图与工作原理2.3、P2 端口结构框图…

73 # 发布自己的 http-server 到 npm

1、添加 .npmignore 文件&#xff0c;忽略不需要的文件 public2、去官网https://www.npmjs.com/检查自己的包名是否被占用 3、切换到官方源&#xff0c;然后检查确认 nrm use npm nrm ls4、登录 npm 账号 npm login5、发布 npm publish6、查看发布情况&#xff0c;发布成功…

波奇学C++:stl的list模拟实现

list是双向带头链表。所以迭代器end()相当于哨兵卫的头。 list不支持和[]重载&#xff0c;原因在于list空间不是连续的&#xff0c;和[]的代价比较大。 访问第n个节点&#xff0c;只能用for循环&#xff0c;来实现 list<int> l; l.push_back(0); l.push_back(1); l.pu…

php检测数组是否存在某个键,和是否存在某个变量

一、array_key_exists() array_key_exists() 是一个 PHP 内置的函数&#xff0c;用于判断数组中是否存在指定的键。该函数接收两个参数&#xff0c;第一个是键名&#xff0c;第二个是数组。 $arr array(name > Jack, age > 20, country > China);if (array_key_exi…

Matlab图像处理-加法运算

加法运算 图像加法运算的一个应用是将一幅图像的内容叠加到另一幅图像上&#xff0c;生成叠加图像效果&#xff0c;或给图像中每个像素叠加常数改变图像的亮度。 在MATLAB图像处理工具箱中提供的函数imadd()可实现两幅图像的相加或者一幅图像和常量的相加。 程序代码 I1 i…