代码随想录算法训练营第三十天 | 重新安排行程、N皇后、解数独

目录

  • 重新安排行程
  • N皇后
  • 解数独
  • 总结

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

重新安排行程

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。

在这里插入图片描述

class Solution {private Deque<String> res;private Map<String, Map<String, Integer>> map;public List<String> findItinerary(List<List<String>> tickets) {map = new HashMap<String, Map<String, Integer>>(); // map<出发机场, map<到达机场, 航班次数>>res = new LinkedList<>();for (List<String> t : tickets) {Map<String, Integer> temp;if (map.containsKey(t.get(0))) {temp = map.get(t.get(0));temp.put(t.get(1), temp.getOrDefault(t.get(1), 0) + 1);} else {temp = new TreeMap<>(); // 升序maptemp.put(t.get(1), 1);}map.put(t.get(0), temp);}res.add("JFK");backTracking(tickets.size());return new ArrayList<>(res);}private boolean backTracking(int ticketNum) {if (res.size() == ticketNum + 1) {return true;}String last = res.getLast();if (map.containsKey(last)) {for (Map.Entry<String, Integer> target : map.get(last).entrySet()) {int count = target.getValue();if (count > 0) {res.add(target.getKey());target.setValue(count - 1);if (backTracking(ticketNum)) return true;res.removeLast();target.setValue(count);}}}return false;}
}

N皇后

时间复杂度: O(n!)
空间复杂度: O(n)

棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度。
在这里插入图片描述

class Solution {List<List<String>> result = new ArrayList<>();public List<List<String>> solveNQueens(int n) {char[][] chessboard = new char[n][n];for (char[] c : chessboard) {Arrays.fill(c, '.');}backTracking(n, 0, chessboard);return result;}private void backTracking(int n, int row, char[][] chessboard) {if (row == n) {result.add(Array2List(chessboard));return;}for (int col = 0; col < n; ++col) {if (isValid(row, col, n, chessboard)) {chessboard[row][col] = 'Q';backTracking(n, row + 1, chessboard);chessboard[row][col] = '.';}}}private List Array2List(char[][] chessboard) {List<String> list = new ArrayList<>();for (char[] c : chessboard) {list.add(String.copyValueOf(c));}return list;}private boolean isValid(int row, int col, int n, char[][] chessboard) {// 列for (int i = 0; i < row; ++i) {if (chessboard[i][col] == 'Q') {return false;}}// 检查135度对角线for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {if (chessboard[i][j] == 'Q') {return false;}}// 检查45度对角线for (int i = row - 1, j = col + 1; i >= 0 && j <= n - 1; i--, j++) {if (chessboard[i][j] == 'Q') {return false;}}return true;}
}

解数独

二维递归(也就是两个for循环嵌套着递归)

一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!

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

在这里插入图片描述

递归函数的返回值需要是bool类型:解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值。

class Solution {public void solveSudoku(char[][] board) {solveSudokuHelper(board);}private boolean solveSudokuHelper(char[][] board) {//「一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,// 一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!」for (int i = 0; i < 9; i++){ // 遍历行for (int j = 0; j < 9; j++){ // 遍历列if (board[i][j] != '.'){ // 跳过原始数字continue;}for (char k = '1'; k <= '9'; k++) {if (isValidSudoku(i, j, k, board)){board[i][j] = k;if (solveSudokuHelper(board)){ // 如果找到合适一组立刻返回return true;}board[i][j] = '.';}}// 9个数都试完了,都不行,那么就返回falsereturn false;// 因为如果一行一列确定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!// 那么会直接返回, 「这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去!」}}// 遍历完没有返回false,说明找到了合适棋盘位置了return true;}/*** 判断棋盘是否合法有如下三个维度:*     同行是否重复*     同列是否重复*     9宫格里是否重复*/private boolean isValidSudoku(int row, int col, char val, 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;}}// 9宫格里是否重复int startRow = (row / 3) * 3;int startCol = (col / 3) * 3;for (int i = startRow; i < startRow + 3; i++){for (int j = startCol; j < startCol + 3; j++){if (board[i][j] == val){return false;}}}return true;}
}

总结

回溯法就是暴力搜索,并不是什么高效的算法,最多再剪枝一下。

回溯算法能解决如下问题:

组合问题:N个数里面按一定规则找出k个数的集合
排列问题:N个数按一定规则全排列,有几种排列方式
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
棋盘问题:N皇后,解数独等等

void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果}
}
  1. 组合问题
    (1)for循环横向遍历,递归纵向遍历,回溯不断调整结果集。
    (2)剪枝:for循环在寻找起点的时候要有一个范围,如果这个起点到集合终止之间的元素已经不够题目要求的k个元素了,就没有必要搜索了。
    (3)如果是一个集合来求组合的话,就需要startIndex;如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex。
    (4)去重:used[i - 1] == true,说明同一树枝candidates[i - 1]使用过;used[i - 1] == false,说明同一树层candidates[i - 1]使用过。

  2. 切割问题
    难点:
    切割问题其实类似组合问题;
    如何模拟那些切割线;
    切割问题中递归如何终止;
    在递归循环中如何截取子串;
    如何判断回文。

  3. 子集问题
    (1)在树形结构中子集问题是要收集所有节点的结果,而组合问题是收集叶子节点的结果。
    (2)注意:result.add(path);要放在终止条件的上面。
    (3)去重和组合中去重是一样的套路,分为树枝去重和树层去重。
    (4)子集问题一定要排序。

  4. 排列问题
    (1)排列是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,这和之前分析的子集以及组合所不同的地方。
    (2)每层都是从0开始搜索而不是startIndex;需要used数组记录path里都放了哪些元素了。
    (3)

  5. 复杂度
    子集问题分析
    时间复杂度: O ( n × 2 n ) O(n × 2^n) O(n×2n),因为每一个元素的状态无外乎取与不取,所以时间复杂度为 O ( 2 n ) O(2^n) O(2n),构造每一组子集都需要填进数组,又有需要 O ( n ) O(n) O(n),最终时间复杂度: O ( n × 2 n ) O(n × 2^n) O(n×2n)
    空间复杂度: O ( n ) O(n) O(n),递归深度为n,所以系统栈所用空间为 O ( n ) O(n) O(n),每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为 O ( n ) O(n) O(n)
    排列问题分析
    时间复杂度: O ( n ! ) O(n!) O(n!),这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * … 1 = n!。每个叶子节点都会有一个构造全排列填进数组的操作(对应的代码:result.push_back(path)),该操作的复杂度为 O ( n ) O(n) O(n)。所以,最终时间复杂度为:n * n!,简化为 O ( n ! ) O(n!) O(n!)
    空间复杂度: O ( n ) O(n) O(n),和子集问题同理。
    组合问题分析
    时间复杂度: O ( n × 2 n ) O(n × 2^n) O(n×2n),组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
    空间复杂度: O ( n ) O(n) O(n),和子集问题同理。
    N皇后问题分析
    时间复杂度: O ( n ! ) O(n!) O(n!) ,其实如果看树形图的话,直觉上是 O ( n n ) O(n^n) O(nn),但皇后之间不能见面所以在搜索的过程中是有剪枝的,最差也就是 O ( n ! ) O(n!) On! n ! n! n!表示 n ∗ ( n − 1 ) ∗ . . . . ∗ 1 n * (n-1) * .... * 1 n(n1)....1
    空间复杂度: O ( n ) O(n) O(n),和子集问题同理。
    解数独问题分析
    时间复杂度: O ( 9 m ) O(9^m) O(9m) , m是’.'的数目。
    空间复杂度: O ( n 2 ) O(n^2) O(n2) , 递归的深度是 n 2 n^2 n2.

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

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

相关文章

信息安全性测试

1 信息安全性测试 信息安全性测试是确保产品或系统能够有效地保护信息和数据&#xff0c;使得用户、其他产品或系统的访问权限与其授权类型和级别相一致的一系列检查过程。信息安全性测试也应该是一个持续的过程&#xff0c;确保信息系统能够抵御恶意攻击&#xff0c;并保护数…

QWB-2018-core | 栈溢出

题目分析 core_write signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3) {unsigned __int64 v3; // rbxv3 a3;printk(&unk_215);if ( v3 < 0x800 && !copy_from_user(&name, a2, v3) )return (unsigned int)v3;printk…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 2月14日,星期三

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年2月14日 星期三 农历正月初五 1、 第十四届全国冬季运动会将于17日开幕&#xff0c;部分赛事今天起陆续开赛。 2、 2024年购房政策将进一步宽松&#xff0c;专家称今年买房性价比更高。 3、 春节档票房突破45亿元&#…

docker 3.1 镜像

docker 3.1 镜像命令 拉取镜像 docker pull debian #从 Docker Hub 拉取名为 debian 的镜像docker pull hello-world #从 Docker Hub 拉入名为 hello-world 的镜像‍ 运行镜像/容器 docker run hello-world ‍ 查看本地所有的镜像 docker images​​ 容器生成镜像…

c++ STL系列——(七)set

在C的标准模板库&#xff08;STL&#xff09;中&#xff0c;set是一个非常有用的容器&#xff0c;用于存储一组按照特定顺序排列的元素&#xff0c;并且不允许重复元素的存在。本文将详细介绍set容器的特点、用法以及一些常用操作&#xff0c;希望对您有所帮助。 Set容器的特点…

【数据结构】链表OJ面试题3《判断是否有环》(题库+解析)

1.前言 前五题在这http://t.csdnimg.cn/UeggB 后三题在这http://t.csdnimg.cn/gbohQ 记录每天的刷题&#xff0c;继续坚持&#xff01; 2.OJ题目训练 9. 给定一个链表&#xff0c;判断链表中是否有环。 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成…

如何使用六图一表七种武器

六图一表七种武器用于质量管理&#xff1a; 描述当遇到问题时应该用那张图来解决&#xff1a; 一、如果题目说出了质量问题需要找原因&#xff1f; 解&#xff1a;用因果图&#xff0c;因果图也称石川图或鱼骨图 二、如果要判断过程是否稳定受控&#xff1f; 解&#xff1a…

4核8G服务器支持多少人同时在线访问?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

Transformer实战-系列教程15:DETR 源码解读2(ConvertCocoPolysToMask类)

&#x1f6a9;&#x1f6a9;&#x1f6a9;Transformer实战-系列教程总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 DETR 算法解读 DETR 源码解读1&#xff08;项目配置/CocoDetection类&#xff09; …

【51单片机】LCD1602(江科大)

1.LCD1602介绍 LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块,可以显示ASCII码的标准字符和其它的一些内置特殊字符,还可以有8个自定义字符 显示容量:162个字符,每个字符为5*7点阵 2.引脚及应用电路 3.内部结构框图 屏幕: 字模库:类似于数码管的数…

代码随想录算法训练营|day31

第八章 贪心算法 455.分发饼干376.摆动序列53.最大子序和代码随想录文章详解 455.分发饼干 对饼干和胃口进行排序 遍历胃口&#xff0c;若当前饼干值<胃口值&#xff0c;饼干继续往后寻找更大值 否则计数1&#xff0c;向后遍历饼干值使其满足下一个胃口值 func findConten…

一起玩儿Proteus仿真(C51)——05. 红绿灯仿真(一)

摘要&#xff1a;本文介绍如何仿真红绿灯 今天来做一个红绿灯仿真的程序&#xff0c;这个程序主要包括一下这些功能&#xff1a; 模拟的路口为十字交叉路口&#xff0c;假设东西和南北方向都是双向行驶&#xff0c;因此需要设置4组红绿灯和4个倒计时显示屏。倒计时时间最长为9…

255.【华为OD机试真题】最小矩阵宽度(滑动窗口算法-JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目二.解题思路三.题解代码Python题解代码JAVA题解…

【教程】C++语言基础学习笔记(七)——Array数组

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【C语言基础学习】系列文章 第一章 《项目与程序结构》 第二章 《数据类型》 第三章 《运算符》 第四章 《流程控制》 第五章…

Linux_进程概念

硬件系统 软件系统 进程概念 进程状态 孤儿进程 进程优先级 一.硬件系统 1.1 冯诺依曼体系结构 数学家冯诺依曼提出了计算机制造的三个基本原则&#xff0c;即采用二进制逻辑、程序存储执行以及计算机由五个部分组成&#xff08;运算器、控制器、存储器、输入设备、输出设备&a…

力扣-28. 找出字符串中第一个匹配项的下标

文章目录 力扣题目代码 力扣题目 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 示例 1&#xff1a…

Python学习之路-爬虫提高:scrapy基础

Python学习之路-爬虫提高:scrapy基础 为什么要学习scrapy 通过前面的学习&#xff0c;我们已经能够解决90%的爬虫问题了&#xff0c;那么scrapy是为了解决剩下的10%的问题么&#xff0c;不是&#xff0c;scrapy框架能够让我们的爬虫效率更高 什么是scrapy Scrapy是一个为了…

适应力和应变力是智能的重要组成部分

适应力和应变力在智能系统中扮演着非常重要的角色。在面对复杂、多变的环境和问题时&#xff0c;一个智能系统需要具备适应和调整自身行为的能力&#xff0c;这就需要依赖适应力和应变力。 适应力指的是系统对环境变化的感知、理解和调整能力。一个具有良好适应力的系统能够及时…

tee漏洞学习-翻译-3:TrustZone exploit for MSM8974

原文&#xff1a;http://bits-please.blogspot.com/2015/08/full-trustzone-exploit-for-msm8974.html 在这篇博文中&#xff0c;我们将介绍利用上一篇文章中描述的 TrustZone 漏洞的完整过程。 在开发此漏洞时&#xff0c;我只使用了我值得信赖的&#xff08;个人&#xff0…

120.乐理基础-五线谱-五线谱的多声部与指法问题

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;119.乐理基础-五线谱-五线谱的标记-CSDN博客 五线谱多声部与简谱的多声部一样&#xff1a;简谱的多声部 五线谱多声部例子&#xff1a;钢琴谱 另一个例子&#xff1a;在纵向上有多个音符 然后放大之后&#xff0c…