代码学习记录25---回溯算法最后一天

news2024/11/16 11:32:26

随想录日记part25【很难

t i m e : time: time 2024.03.21



主要内容:回溯算法在之前的学习中已经熟练掌握,今天对其进行挑战并进行总结:1:重新安排行程 ;2.N皇后 ;3.解数独

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


Topic1重新安排行程

题目:

给你一份航线列表 t i c k e t s tickets tickets ,其中 t i c k e t s [ i ] = [ f r o m i , t o i ] tickets[i] = [fromi, toi] tickets[i]=[fromi,toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。所有这些机票都属于一个从 J F K JFK JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 J F K JFK JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。例如,行程 [ " J F K " , " L G A " ] ["JFK", "LGA"] ["JFK","LGA"] [ " J F K " , " L G B " ] ["JFK", "LGB"] ["JFK","LGB"] 相比就更小,排序更靠前。假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。
在这里插入图片描述
输入: t i c k e t s = [ [ " M U C " , " L H R " ] , [ " J F K " , " M U C " ] , [ " S F O " , " S J C " ] , [ " L H R " , " S F O " ] ] tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]] tickets=[["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
输出: [ " J F K " , " M U C " , " L H R " , " S F O " , " S J C " ] ["JFK","MUC","LHR","SFO","SJC"] ["JFK","MUC","LHR","SFO","SJC"]

思路:

这道题目有几个难点:

  • 一个行程中,如果航班处理不好容易变成一个圈,成为死循环
  • 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
  • 使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?
  • 搜索的过程中,如何遍历一个机场所对应的所有机场。
    [ [ " J F K " , " K U L " ] , [ " J F K " , " N R T " ] , [ " N R T " , " J F K " ] [["JFK", "KUL"], ["JFK", "NRT"], ["NRT", "JFK"] [["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]为例,抽象为树形结构如下:
    按照回溯模板我们进行回溯三部曲:
    在这里插入图片描述

递归三部曲:

1.回溯函数模板返回值以及参数
在这里要定义一个全局变量 r e s u l t result result用来存放符合条件结果的集合。一个全局变量 p a t h path path存放某一种可能行程。一个全局变量 u s e d [ ] used[] used[],表示某张票是否使用。
所以整体代码如下:

List<List<String>> result = new ArrayList<>();// 存放所有的可能行程
List<String> path = new LinkedList<>();// 存放某一种可能行程
boolean[] used;
void reback(ArrayList<List<String>> tickets)

2.回溯函数终止条件
p a t h path path保存的地点数量和应该输出的节点数一致时表示找到其中的一种可能。
代码如下:

if (tickets.size() + 1 == path.size()) {
            result.add(new ArrayList(path));
            return;
        }

3.回溯搜索的遍历过程
然后就是递归和回溯的过程,实现代码思路简单实现如下:

   if (used[i] == false && tickets.get(i).get(0).equals(path.getLast())) {
                used[i] = true;
                path.add(tickets.get(i).get(1));
                reback(tickets);
                path.removeLast();
                used[i] = false;
            }

我将所有的可能都存在了 r e s u l t result result中,我们要输出的是字典序最小的那种可能,所以我对其进行字典序重排操作:

// 按字典序排序
        Collections.sort(result, (list1, list2) -> {
            Iterator<String> iter1 = list1.iterator();
            Iterator<String> iter2 = list2.iterator();
            while (iter1.hasNext() && iter2.hasNext()) {
                int comparison = iter1.next().compareTo(iter2.next());
                if (comparison != 0) {
                    return comparison;
                }
            }

最后输出 r e s u l t . g e t ( 0 ) result.get(0) result.get(0)

完整的代码如下:

class Solution {
    List<List<String>> result = new ArrayList<>();// 存放所有的可能行程
    List<String> path = new LinkedList<>();// 存放某一种可能行程
    boolean[] used;
    public List<String> findItinerary(List<List<String>> tickets) {
        used = new boolean[tickets.size()];
        Arrays.fill(used, false);
        path.add("JFK");
        reback((ArrayList) tickets);
        // 按字典序排序
        Collections.sort(result, (list1, list2) -> {
            Iterator<String> iter1 = list1.iterator();
            Iterator<String> iter2 = list2.iterator();
            while (iter1.hasNext() && iter2.hasNext()) {
                int comparison = iter1.next().compareTo(iter2.next());
                if (comparison != 0) {
                    return comparison;
                }
            }
            return Integer.compare(list1.size(), list2.size());
        });
        return result.get(0);//选取第一个输出
    }

    public void reback(ArrayList<List<String>> tickets) {
        if (tickets.size() + 1 == path.size()) {
            result.add(new ArrayList(path));
            return;
        }
        for (int i = 0; i < tickets.size(); i++) {
            if (used[i] == false && tickets.get(i).get(0).equals(path.getLast())) {
                used[i] = true;
                path.add(tickets.get(i).get(1));
                reback(tickets);
                path.removeLast();
                used[i] = false;
            }
        }
    }
}


Topic2N皇后

题目:

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n n n 皇后问题研究的是如何将 n n n 个皇后放置在 n × n n×n n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数 n n n ,返回所有不同的 n n n 皇后问题的解决方案。
每一种解法包含一个不同的 n n n 皇后问题的棋子放置方案,该方案中 ′ Q ′ 'Q' Q ′ . ′ '.' . 分别代表了皇后和空位。
在这里插入图片描述

输入: n = 4 n = 4 n=4
输出: [ [ " . Q . . " , " . . . Q " , " Q . . . " , " . . Q . " ] , [ " . . Q . " , " Q . . . " , " . . . Q " , " . Q . . " ] ] [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]] [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]

思路:

按照回溯模板我们进行回溯三部曲:
递归三部曲:
1.回溯函数模板返回值以及参数
在这里要定义一个全局变量 r e s u l t result result用来存放符合条件结果的集合。但是中途时使用了一个棋盘数组 c h e s s b o a r d chessboard chessboard

所以整体代码如下:

List<List<String>> result=new ArrayList<>();//保存结果
void reback(int n,int q,char[][] chessboard)

2.回溯函数终止条件
可以看出,当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了
在这里插入图片描述

if(q==n){
	result.add(Array2List(chessboard));
    return;
}

3.回溯搜索的遍历过程
递归深度就是 q q q控制棋盘的行,每一层里 f o r for for循环 i i i控制棋盘的列,一行一列,确定了放置皇后的位置。每次都是要从新的一行的起始位置开始搜,所以都是从0开始

 for(int i=0;i<n;i++){
            if(iscan(q,i,n,chessboard)){
                chessboard[q][i]='Q';
                reback(n,q+1,chessboard);
                chessboard[q][i]='.';
            }
        }  

完整的代码如下:

class Solution {
    List<List<String>> result = new ArrayList<>();// 保存结果

    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                chessboard[i][j] = '.';
            }
        }
        reback(n, 0, chessboard);
        return result;

    }

    public List Array2List(char[][] chessboard) {// char[][]转换为String
        List<String> res = new ArrayList<>();
        for (char[] c : chessboard) {
            res.add(String.copyValueOf(c));
        }
        return res;
    }

    public boolean iscan(int x, int y, int n, char[][] chessboard) {
        for (int i = 0; i < y; i++) {
            if (chessboard[x][i] == 'Q')
                return false;
        }
        for (int i = 0; i < x; i++) {
            if (chessboard[i][y] == 'Q')
                return false;
        }
        for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {
            if (chessboard[i][j] == 'Q')
                return false;
        }
        for (int i = x - 1, j = y + 1; i >= 0 && j < n; i--, j++) {
            if (chessboard[i][j] == 'Q')
                return false;
        }
        return true;

    }

    public void reback(int n, int q, char[][] chessboard) {
        if (q == n) {
            result.add(Array2List(chessboard));
            return;
        }
        for (int i = 0; i < n; i++) {
            if (iscan(q, i, n, chessboard)) {
                chessboard[q][i] = 'Q';
                reback(n, q + 1, chessboard);
                chessboard[q][i] = '.';
            }
        }
    }
}

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



Topic3解数独

题目:

编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
1.数字 1-9 在每一行只能出现一次。
2.数字 1-9 在每一列只能出现一次。
3.数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
在这里插入图片描述

输入: b o a r d = [ [ " 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 " ] ] 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"]] 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 " ] ] [["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"]] [["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"]]

思路:

按照回溯模板我们进行回溯三部曲:
递归三部曲:
1.回溯函数模板返回值以及参数
所以整体代码如下:

boolean iscan(char i,int x,int y,char[][] board)

2.回溯函数终止条件
本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。

3.回溯搜索的遍历过程
在这里插入图片描述
一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!

 for(int x=0;x<9;x++){
            for(int y=0;y<9;y++){
                if(board[x][y]!='.')continue;
                for(char i='1';i<='9';i++){
                    if(iscan(i,x,y,board)){
                        board[x][y]=i;
                        if(reback(board)==true) return true;
                        board[x][y]='.';
                    }
                }
                return false;
            }

        }

完整的代码如下:

class Solution {
    public void solveSudoku(char[][] board) {
        boolean res = reback(board);
    }

    public boolean iscan(char i, int x, int y, char[][] board) {
        for (int j = 0; j < 9; j++) {
            if (board[x][j] == i)
                return false;
        }
        for (int j = 0; j < 9; j++) {
            if (board[j][y] == i)
                return false;
        }
        int starx = x / 3 * 3;
        int stary = y / 3 * 3;
        for (int p = starx; p < starx + 3; p++) {
            for (int q = stary; q < stary + 3; q++) {
                if (board[p][q] == i)
                    return false;
            }
        }
        return true;
    }

    private boolean reback(char[][] board) {
        for (int x = 0; x < 9; x++) {
            for (int y = 0; y < 9; y++) {
                if (board[x][y] != '.')
                    continue;
                for (char i = '1'; i <= '9'; i++) {
                    if (iscan(i, x, y, board)) {
                        board[x][y] = i;
                        if (reback(board) == true)
                            return true;
                        board[x][y] = '.';
                    }
                }
                return false;
            }

        }
        return true;
    }

}

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

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

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

相关文章

SpringJPA 做分页条件查询

前言: 相信小伙伴们的项目很多都用到SpringJPA框架的吧,对于单表的增删改查利用jpa是很方便的,但是对于条件查询并且分页 是不是很多小伙伴不经常写到. 今天我整理了一下在这里分享一下. 话不多说直接上代码: Controller: RestController public class ProductInstanceContr…

Java基础-正则表达式

文章目录 1.基本介绍2.正则底层实现1.matcher.find()完成的任务2.matcher.group(0)分析1.源代码2.解释&#xff08;不分组&#xff09;3.解释&#xff08;分组&#xff09; 3.总结 3.正则表达式语法1.基本介绍2.元字符的转义符号1.基本介绍2.代码实例 3.字符匹配符1.基本介绍2.…

洛谷day3

B2053 求一元二次方程 - 洛谷 掌握printf用法&#xff1b; #include <iostream> #include <cmath> using namespace std; double a,b,c; double delta; double x1,x2;int main() {cin>>a>>b>>c;delta b*b-4*a*c;if(delta>0){x1 (-bsqrt…

【前端寻宝之路】JavaScript初学之旅

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-azUa9yH16cRXQUxE {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

生产力工具|安装更新R软件(R、studio)

内容介绍&#xff1a; 安装R软件&#xff1a; 下载 R X64 3.5.1: 访问官方R网站 https://cran.r-project.org/。选择适合Windows版本的安装包。将安装包下载到您的计算机。 本地安装: 运行下载的“R-3.5.1-win.exe”文件。按照安装向导&#xff0c;选择安装路径&#xff0c;取消…

机器视觉定位点胶机系统工作原理分析

机器视觉&#xff0c;简而言之&#xff0c;即是通过机器来模拟人眼的视觉功能&#xff0c;执行测量与判断的任务。这一过程中&#xff0c;关键的角色是机器视觉产品&#xff0c;它们通常以CMOS或CCD的形式出现&#xff0c;负责将观察的目标转化为图像信号。这些信号随后被专用的…

Pinctrl子系统_05_Pincontroller构造过程情景分析

上一节我们了解了Pinctrl子系统主要的数据结构&#xff0c;要想更好的掌握Pinctrl子系统&#xff0c;还需要知道他的构造过程。 本节我们就来分析一下Pinctrl子系统的构造过程。 以内核面向对象的思想&#xff0c;设备树可以分为两部分&#xff0c;左边是Pinctrl子系统节点&a…

nodejs+vue高校师资管理系统python-flask-django-php

快速发展的社会中&#xff0c;人们的生活水平都在提高&#xff0c;生活节奏也在逐渐加快。为了节省时间和提高工作效率&#xff0c;越来越多的人选择利用互联网进行线上打理各种事务&#xff0c;然后线上管理系统也就相继涌现。与此同时&#xff0c;人们开始接受方便的生活方式…

002- 用 AutoCoder 添加和修改代码

实际上 AutoCoder 最适合的场景是修改代码&#xff0c;因为它最原始的功能是把源码你指定的文档搜索引擎搜集到的资料和你的需求生成一个prompt给到大模型。 此外纠正大家一个观点&#xff0c; AutoCoder 不是一句话给你创建一个网站抑或一个大型的项目。我们是帮助研发或者产品…

移植 Zephyr 到 Art-Pi

背景 ​ 最近工作中接触到了 Zephyr&#xff0c;不由觉得 Zephyr 是个很强大、全面、优秀的实时操作系统&#xff0c;但同时是有一定的上手难度的&#xff0c;其复杂的构建系统让小编倒吸一口凉气。为了深入研究并完全掌控 Zephyr&#xff0c;小编决定把它移植到手头的开发板上…

Java八股文(SpringCloud)

Java八股文のSpringCloud SpringCloud SpringCloud 什么是Spring Cloud&#xff1f; Spring Cloud是一个用于构建分布式系统的开发工具箱&#xff0c;它基于Spring Boot框架&#xff0c;提供了一系列的组件和工具&#xff0c;用于帮助开发者快速搭建和管理分布式系统中的各种常…

Spring相关框架八股

单例bean是线程安全的吗&#xff1f; AOP 事务失效 Bean生命周期 Bean循环依赖解决 MVC执行流程 自动装配原理 Spring常见注解 SpringMVC注解 SpringBoot注解 MyBatis执行流程 MyBatis延迟加载 MyBatis缓存 SpringCloud五大组件 注册中心Nacos、Eureka 负载均衡Ribbon 服务雪崩…

Android 你遇到的无障碍onGesture不执行

你是不是和我一样&#xff0c;在开发无障碍应用的时候&#xff0c;翻边了Android的AccessibilityService源码 但是就是发现不了onGesture不执行的原因&#xff1f; 你是不是和我一样&#xff0c;在好多测试手机之间徘徊&#xff0c;发现还是不执行&#xff1f; 你是不是和我一…

2024内蒙古等保备案办理流程指引

网络安全已成为国家、企业和个人共同关注的焦点。所以积极办理等保备案是非常重要的。本文将详细介绍办理内蒙古企业办理等保备案的流程&#xff0c;帮助读者了解并顺利完成相关手续。仅供参考哦&#xff01; 2024内蒙古等保备案办理流程指引 办理时间&#xff1a;周一到周五工…

PostgreSQL11 | Windows系统安装PostgreSQL

本教程选取与参考书籍《PostgreSql11 从入门到精通》&#xff08;清华大学出版社&#xff09;的11大版本最新小版本11.22的安装作为教程案例 下载 下载PostgreSQL installer 下载到本地 安装 运行安装引导器 中国地区语言选项&#xff08;暂时&#xff09; Chinese(Simplifie…

OpenHarmony IDL工具规格及使用说明书(仅对系统应用开放)

IDL接口描述语言简介 当客户端和服务器进行IPC通信时&#xff0c;需要定义双方都认可的接口&#xff0c;以保障双方可以成功通信&#xff0c;OpenHarmony IDL&#xff08;OpenHarmony Interface Definition Language&#xff09;则是一种定义此类接口的工具。OpenHarmony IDL先…

io流的学习4

字符缓冲流 原理&#xff1a;底层自带了长度为8192的缓冲区提高性能。 import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;public class BufferedStringdemo01 {public static void main(String…

【旅游景点项目日记 | 第二篇】基于Selenium爬取携程网景点详细数据

文章目录 3.基于Selenium爬取携程网景点详细数据3.1前提环境3.2思路3.3代码详讲3.3.1查询指定城市的所有景点3.3.2获取详细景点的访问路径3.3.3获取景点的详细信息 3.4数据库设计3.5全部代码3.6效果图 3.基于Selenium爬取携程网景点详细数据 3.1前提环境 确保安装python3.x环…

HCIP的学习(4)

GRE和MGRE VPN---虚拟专用网络。指依靠ISP或其他公有网络基础设施上构建的专用的安全数据通信网络。该网络是属于逻辑上的。​ 核心机制—隧道机制&#xff08;封装技术&#xff09; GRE—通用路由封装 ​ 三层隧道技术&#xff0c;并且是属于点到点的隧道。 [r1]interface T…

1+x中级题目练习复盘(九)

注解和注释是两种完全不同的语法&#xff0c;注解可以为程序增加额外的功能&#xff0c;或为程序添加元数据。 函数式接口是指有且只有一个抽象方法的接口&#xff1b; 函数式接口可以使用 FunctionalInterface 进行标注&#xff0c;但不是必须的。除了 “java.util.function…