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

news2024/9/22 1:07:29

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

  • ● Leetcode332 重新安排行程
    • ● 本题特点
    • ● 解题思路
    • ● 代码实现
  • ● Leetcode51 N皇后
    • ● 本题特点
    • ● 解题思路
    • ● 代码实现
  • ● Leetcode37 解数独
    • ● 本题特点
    • ● 解题思路
    • ● 代码实现

● Leetcode332 重新安排行程

题目链接:[Leetcode332 重新安排行程]
视频讲解:[代码随想录|重新安排行程]
题目描述:

● 本题特点

重新安排行程更像是深度优先搜索,但也是深度优先搜索中使用回溯,在查找路径的过程中需要用到回溯。

对于重新安排形成,我们需要注意:
(1)应该避免在处理航班形成的过程形成一个圈,成为死循环的情况;
(2)题目提示中如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前,字母序靠前应该排在前面,如何记录映射关系;
(3)使用确定回溯条件以及在搜索过程中,如何遍历一个机场对应的所有机场。

● 解题思路

(1)记录映射关系:
一个机场映射多个机场,机场之间靠字母字典序排列,可以使用std::unordered_map,如果多个机场之间有顺序则使用std::map/std::multimap/std::multiset
将映射关系定义为:unordered_map<string, multimap<string, int>> targets,multimap<string, int>> targets表示unordered_map<出发机场, 到达机场的集合> targets,unordered_map<string, map<string, int>> targets表示unordered_map<出发机场, map<到达机场, 航班次数>> targets,这里不使用multiset是为了防止删除元素后迭代器失效;
(2)避免死循环:
使用unordered_map是为了方便我们在过程中增删元素,因为出发机场和到达机场是会重复的,搜索过程每及时删除目的机场就会导致死循环,在删除过程中可以使用航班次数这个子段做增减,标记到达机场是否使用过;

i.确定回溯函数的参数和返回值:
参数传入ticketNum表示有多少个航班,返回值使用bool,我们只需要找到一个形成,在树形结构中对应唯一的一条通向叶子节点的路线;
ii.确定回溯函数的终止条件:
由示例[[“MUC”, “LHR”], [“JFK”, “MUC”], [“SFO”, “SJC”], [“LHR”, “SFO”]] ,这是有4个航班,那么只要找出一种行程,行程里的机场个数是5就可以了,也就是回溯遍历过程中,如果达到了(航班数量+1),就找到了一个形成课可以把所有航班穿起来;
iii.确定回溯函数单层遍历逻辑:

    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++;
        }
    }

● 代码实现

class Solution {
private:
// unordered_map<出发机场, map<到达机场, 航班次数>> targets
unordered_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;
}
public:
    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;
    }
};

● Leetcode51 N皇后

题目链接:Leetcode51 N皇后
视频讲解:代码随想录|N皇后
题目描述:按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

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

提示:
1 <= n <= 9

● 本题特点

对于N皇后的解决同样可以构造成递归过程的树形结构,只不过不同于之前组合、分割、子集、子序列问题,在每一个结点都是一个chessboard结构,我们在chessboard中填入皇后Q,并使其满足同行、同列以及45°、135°位置不存在任何皇后,保证chessboard上皇后们的和谐;树的深度取决于chessboard的规模。

● 解题思路

首先我们需要解决N皇后在空格填入皇后的检验,也就是检验棋盘是否合法:
(1)不能同行:
因为在单层搜索的过程中,每一层递归,只会选for循环里的一个元素,所以对于行而言不需要做检验操作;
(2)不能同列:
检查同列上是否存在皇后,该过程我们传入的列col是不变了,需要循环遍历行row检查是否有chessboard[i][col] == Q是否满足;

	for (int i = 0; i < row; i++) { // 这是一个剪枝
        if (chessboard[i][col] == 'Q') {
            return false;
        }
    }

(3)不能同斜线:
同斜线包括两部分:45°和135°。
45°在第一象限,因此我们需要检测的是[row - 1, col + 1]向右上方延申的格子是否有皇后;

    for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }

135°在第二象限,因此我们需要检测的是[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;
        }
    }

i.确定回溯函数的参数和返回值:
定义全局二维数组result记录最终结果,参数n是chessboard的大小,row记录当前遍历第几层;
ii.确定回溯函数的终止条件:
当遍历到最后一行时,即row == n时,遍历结束;
iii.确定回溯函数单层遍历逻辑:
递归深度就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,一行一列,确定了放置皇后的位置。

● 代码实现

class Solution {
private:
    vector<vector<string>> result;

    void backtracking(vector<string>& chessboard, int n, int row)
    {
        if(row == n)
        {
            result.push_back(chessboard);
            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] = '.';
            }
        }
    }

    bool isValid(vector<string>& chessboard, int n, int row, int col)
    {
        //同列检查
        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 < n; --i, ++j)
        {
            if(chessboard[i][j] == '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;
            }
        }
        return true;
    }
public:
    vector<vector<string>> solveNQueens(int n) {
        vector<string> chessboard(n, string(n, '.'));
        backtracking(chessboard, n, 0);
        return result;
    }
};

● Leetcode37 解数独

题目链接:Leetcode37 解数独
视频讲解:代码随想录|解数独
题目描述:编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例 1:
数独
输入: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”]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
数独

提示:
board.length == 9
board[i].length == 9
board[i][j] 是一位数字或者 ‘.’
题目数据 保证 输入数独仅有一个解

● 本题特点

不同于N皇后的问题,数独问题的解决需要使用二维递归,一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性不仅需要判断行是否满足条件,还需要列也满足条件,本题每一个位置都需要放入一个数字,并检查数字摆放的合法性,因此解数独的树形结构比N皇后更宽更深。

● 解题思路

i.确定回溯函数的参数和返回值:
本题回溯函数返回值为bool类型,因为解数独找到一个符合条件就立刻返回,相当于找从根结点到叶子结点的一条唯一路径;
ii.确定回溯函数的终止条件:
当遍历到最后一行一列的时候,也就是将数独填写完成,循环自动退出,回溯函数也自然退出
iii.确定回溯函数单层遍历逻辑:
一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性。
注意这里return false的地方:因为如果一行一列确定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解;那么会直接返回, 这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去。

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] != '.') continue;
            for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适
                if (isValid(i, j, k, board)) {
                    board[i][j] = k;                // 放置k
                    if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                    board[i][j] = '.';              // 回溯,撤销k
                }
            }
            return false;                           // 9个数都试完了,都不行,那么就返回false
        }
    }
    return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}

● 代码实现

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++)
                    {
                        if(isValid(board, i, j, k))
                        {
                            board[i][j] = k;
                            if(backtracking(board)) return true;
                            board[i][j] = '.';
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }

    bool isValid(vector<vector<char>>& board, int row, int col, char k)
    {
        //检查行
        for(int i = 0; i < 9; i++)
        {
            if(board[row][i] == k)
            {
                return false;
            }
        }
        //检查列
        for(int j = 0; j < 9; j++)
        {
            if(board[j][col] == k)
            {
                return false;
            }
        }
        //检查九宫格
        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] == k)
                {
                    return false;
                }
            }
        }
        return true;
    }
public:
    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
    }
};

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

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

相关文章

Hashtable 是如何保证线程安全的?

1、典型回答 Hashtable 保证线程安全主要是通过给关键方法&#xff0c;例如 put 添加方法、remove 删除方法&#xff0c;添加 synchronized 加锁来保证线程安全的。 2、全面剖析 Hashtable 保证线程安全的方法实现非常简单粗暴&#xff0c;就是给关键方法整体添加 synchroni…

Linux 理解文件系统、磁盘结构、软硬链接

目录 一、理解磁盘结构 1、磁盘的物理结构 2、硬件层面理解 3、磁盘的具体物理存储结构 4、进行逻辑抽象 5、磁盘文件的管理 6、创建新文件的过程 二、理解文件系统 1、文件的构成 2、为何选择4KB而非512字节作为基本单位? 3、文件系统的组成 数据块&#xff08;Data Blocks&a…

LeetCode每日一题[c++]-322.零钱兑换

题目描述 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数量是无…

多线程合并练习题,线程安全(售票任务引入)--学习JavaEE的day30

day30 练习&#xff08;day29&#xff09; 注意代码注释&#xff0c;里面涉及代码实现遇到问题及解决方案&#xff0c;由于理解方便没有单独出来 1.计算任务 1.计算任务&#xff0c;一个包含了2万个整数的数组&#xff0c;分拆了多个线程来进行并行计算&#xff0c;最后汇总出…

敏捷开发——第二次作业JS/服务器的部署

部署 Web 服务器 1. 安装 Apache HTTP 服务器并部署静态网页应用 ⭐⭐ 默认情况下&#xff0c;Apache 在 /var/www/html 目录下寻找要提供服务的文件。可以将静态网页文件放置在这个目录下 2.安装 Nginx 并部署静态页面应用 3. 实践部分 1. 2. 3. 在 /var/www/html 目录下…

【算法 高级数据结构】树状数组:一种高效的数据结构(二)

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;专栏&#xff1a;算法题、 基础算法、数据结构~赶紧来学算法吧 &#x1f4a1;往期推荐&#xff1a; 【算法基础 & 数学】快速幂求逆元&#xff08;逆元、扩展欧几里得定理、小费马定…

kingbase 归档日志

开启归档 archive_mode on archive_commandtest ! -f /home/archive_kb/%f && cp %p /home/archive_kb/%f 注意&#xff1a;修改后需要重启&#xff0c;archive_command 这里设置的是 归档日志文件存储在 归档日志路径 /home/archive_kb 生成归档文件 手动切换 [kin…

茶饮品牌抖音账号规划流量运营策划方案

【干货资料持续更新&#xff0c;以防走丢】 茶饮品牌抖音账号规划流量运营策划方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 抖音运营资料合集&#xff08;完整资料包含以下内容&#xff09; 目录 冷启动期 1. 直播前期准备 - 进行DOUA/B测试&#xff0…

Unity-UGUI系统

UGUI是什么 UGUI是Unity引擎内自带的UI系统官方称之为:Unity Ul 是目前Unity商业游戏开发中使用最广泛的UI系统开发解决方案 它是基于Unity游戏对象的UI系统&#xff0c;只能用来做游戏UI功能 不能用于开发Unity编辑器中内置的用户界面 六大基础组件 概述 Canvas EventS…

Java基于微信小程序的助农扶贫系统的研究与实现

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#…

远程控制手机关闭空调,轻松省下一笔电费!存起来夏天用

随着科技的不断发展&#xff0c;人们对于节能减排的需求也越来越高。在这个过程中&#xff0c;远程操控空调成为了一种新兴的节能技巧。通过远程操控空调&#xff0c;我们可以更加精确地控制室内温度&#xff0c;从而实现节能减排的目标。本文将详细介绍远程操控空调的优势以及…

3.25C++

定义自己的命名空间&#xff0c;其中有string类型的变量&#xff0c;再定义两个函数&#xff0c;一个函数完成字符串的输入&#xff0c;一个函数完成求字符串长度&#xff0c;再定义一个全局函数完成对该字符串的反转 #include <iostream> #include <cstring> usi…

Linux 服务升级:Nginx 热升级 与 平滑回退

目录 一、实验 1.环境 2.Kali Linux 使用nmap扫描CentOS 3.Kali Linux 远程CentOS 4.Kali Linux 使用openvas 扫描 CentOS 5.Nginx 热升级 6.Nginx 平滑回退 二、问题 1.kill命令的信号有哪些 2.平滑升级与回退的信号 一、实验 1.环境 &#xff08;1&#xff09;主机…

电路笔记 :嘉立创EDA常用功能

原理图 翻转和旋转 嘉立创连线快捷键&#xff1a;Alt W 原理图转PCB 交叉选择&#xff08;切换至PCB并高亮选中的部分&#xff09;快捷键&#xff1a;“SHIFT X” PCB pcb边框 画pcb边框&#xff1a;放置-》板框-》矩形 挖槽 布线 pcB检查 出错的地方会有小叉子 等长调节…

NKCTF--pwn--Maimai查分器

NKCTF–pwn–Maimai查分器 Maimai查分器 保护全开 存在格式化字符串漏洞 第一步&#xff1a;先测速率&#xff0c;输入15.0 SSS 50次获得最高速率 ​ sl(b1) #debug() for i in range(50):sl(b15.0 SSS)然后利用格式化字符串去泄露&#xff0c;本来想一口气全部泄露的&…

继承和多态(2)(多态部分)

提前讲的重要知识点 一个类在没有父类的情况下默认有一个父类为Object类。 而当在有父类情况下&#xff0c;如果你那父类没有父类&#xff0c;则其父类的父类默认为object类&#xff0c;所以即使一个类有父类&#xff0c;其内部还是有object类。 object类都是隐藏起来的&…

浅谈C++引用的使用以及底层原理

文章目录 1、引用概念2、引用特性3、常引用4、引用的使用场景&#xff08;1&#xff09;做参数&#xff08;2&#xff09;做返回值 1、引用概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和…

链式二叉树经典OJ题目(一)

目录 结构体声明&#xff1a; 1.单值二叉树 题目描述&#xff1a; 思路分析&#xff1a; 源码&#xff1a; 2.二叉树最大深度 题目描述&#xff1a; 思路分析&#xff1a; 源码&#xff1a; 3.检查两棵树是否相同 题目描述&#xff1a; 思路分析&#xff1a; 源码…

EMC | 浪涌保护电路NTC

NTC(5D-11)负温度系数热敏电阻。 NTC是过流器件。 抑制开机的浪涌电流&#xff0c;NTC温度越高&#xff0c;电阻越低。 如果没有NTC,220VAC开机对电容CE3充电&#xff0c;此时电容中没有电荷&#xff0c;CE3相当于短路&#xff0c;回路电流会很大。 选型注意 1、R25温度下电阻…

Negative Sampling with Adaptive DenoisingMixup for Knowledge Graph Embedding

摘要 知识图嵌入(Knowledge graph embedding, KGE)的目的是通过对比正负三元组&#xff0c;将知识图中的实体和关系映射到一个低维、密集的向量空间中。在kge的训练过程中&#xff0c;由于kge只包含正三元组&#xff0c;因此负采样对于找到高质量的负三元组至关重要。大多数现…