回溯算法经典面试题

news2024/11/24 17:00:26

请添加图片描述
⭐️前言⭐️

本文汇总了常见的回溯算法题目,并将框架来进行运用,相信通过这篇文章,读者能够对回溯算法有一定了解。

🍉欢迎点赞 👍 收藏留言评论 📝私信必回哟😁

🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

🍉博客中涉及源码及博主日常练习代码均已上传GitHub


请添加图片描述

📍内容导读📍

  • 🍅1.回溯算法框架
  • 🍅2.经典题目练习
    • 2.1 全排列问题
    • 2.2 N皇后问题

🍅1.回溯算法框架

解决一个回溯问题,实际上就是一个决策树的遍历过程,站在回溯树的一个节点上,你只需要考虑3个问题:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
接下来通过全排列N皇后的经典回溯问题来应用理解这个框架。

代码框架如下:

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

核心是for循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」。

🍅2.经典题目练习

2.1 全排列问题

https://leetcode.cn/problems/permutations/
在这里插入图片描述
n个不重复的数,全排列共有n!个。
下图中[3]就是路径,记录你已经做过的选择;[1,2]就是「选择列表」,表示你当前可以做出的选择;「结束条件」就是遍历到树的底层叶子节点,这里也就是选择列表为空的时候。
在这里插入图片描述
我们定义的backtrack函数就像一个指针,在这棵树上游走,同时要正确维护每个节点的属性,每当走到树的叶子节点,其路径就是一个全排列。

代码实现:

class Solution {
    List<List<Integer>> res=new LinkedList<>();
    // 主函数,输入一组不重复的数字,返回他们的全排列。
    public List<List<Integer>> permute(int[] nums) {
    	//记录路径
        LinkedList<Integer> track=new LinkedList<>();
        //路径中的元素会被标记为true,避免重复使用
        boolean[] used=new boolean[nums.length];
        backtrack(nums,track,used);
        return res;
    }
    // 路径记录在track中
    // 选择列表:nums中不存在于track的那些元素
    // 结束条件:nums中的元素全都在track中出现
    void backtrack(int[] nums,LinkedList<Integer> track,boolean[] used) {
    	//触发结束条件
        if(track.size()==nums.length) {
            res.add(new LinkedList(track));
            return;
        }
        for(int i=0;i<nums.length;i++) {
        	// 排除不合法的选择
            if(used[i]) continue;
            // 做选择
            track.add(nums[i]);
            used[i]=true;
            backtrack(nums,track,used);
            // 取消选择
            used[i]=false;
            track.removeLast();
        }
    }
}

https://leetcode.cn/problems/permutations-ii/description/
在这里插入图片描述
注意:
该题目与上题相比,存在了重复的元素,所以在保存结果的时候,不需要保存数字结果相同的结果,那么这个时候就需要我们剪枝,比如[1,2,2 '] 路径记录下来以后,路径[1,2 ',2]就不会被记录。

如果涉及考虑重复元素,或者大小比较的情况,首先应该对列表进行排序,然后再去进一步记录结果。

那么针对重复元素造成结果相同的这部分剪枝的条件为:和前一个元素值相同(此处隐含这个元素的index>0),并且前一个元素还没有被使用过。

比如[1,2,2 '] 路径记录下来以后,路径[1,2 ‘,2]首先记录2’,在数组中的排序为1,2,2’,因为2’与数组中前一个数值相同,并且没被用过,所以该路径直接被剪枝。

即代码:

if(i>0&&nums[i-1]==nums[i]&&!used[i-1]) continue;

总代码实现:

class Solution {
    List<List<Integer>> res=new LinkedList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        LinkedList<Integer> track=new LinkedList<>();
        boolean[] used=new boolean[nums.length];
        Arrays.sort(nums);
        backtrack(nums,track,used);
        return res;
    }
    void backtrack(int[] nums,LinkedList<Integer> track,boolean[] used) {
        if(track.size()==nums.length) {
            res.add(new LinkedList(track));
            return;
        }
        for(int i=0;i<nums.length;i++) {
            if(used[i]) continue;
            if(i>0&&nums[i-1]==nums[i]&&!used[i-1]) continue;
            track.add(nums[i]);
            used[i]=true;
            backtrack(nums,track,used);
            used[i]=false;
            track.removeLast();
        }
    }
}

2.2 N皇后问题

在这里插入图片描述
题解思路:
该题其实也和上边的全排列相似,也是回溯问题,决策树的每一层表示棋盘上的每一行;每个节点可以做出的选择是,在该行的任意一列放置一个皇后。
剪枝的条件是:
判断是否符合规则,该位置的上方、左上方和右上方是否有皇后。
代码实现:

class Solution {
    List<List<String>> res = new ArrayList<>();

    /* 输入棋盘边长 n,返回所有合法的放置 */
    public List<List<String>> solveNQueens(int n) {
        // '.' 表示空,'Q' 表示皇后,初始化空棋盘
        List<String> board = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < n; j++) {
                sb.append('.');
            }
            board.add(sb.toString());
        }
        backtrack(board, 0);
        return res;
    }

    // 路径:board 中小于 row 的那些行都已经成功放置了皇后
    // 选择列表:第 row 行的所有列都是放置皇后的选择
    // 结束条件:row 超过 board 的最后一行
    void backtrack(List<String> board, int row) {
        // 触发结束条件
        if (row == board.size()) {
            res.add(new ArrayList<>(board));
            return;
        }
        
        int n = board.get(row).length();
        for (int col = 0; col < n; col++) {
            // 排除不合法选择
            if (!isValid(board, row, col)) {
                continue;
            }
            // 做选择
            StringBuilder sb = new StringBuilder(board.get(row));
            sb.setCharAt(col, 'Q');
            board.set(row, sb.toString());

            // 进入下一行决策
            backtrack(board, row + 1);
            // 撤销选择
            sb.setCharAt(col, '.');
            board.set(row, sb.toString());
        }
    }

    /* 是否可以在 board[row][col] 放置皇后? */
    boolean isValid(List<String> board, int row, int col) {
        int n = board.size();

        /* 检查上方是否有皇后互相冲突 */
          for(int i=0;i<row;i++) {
            if(board.get(i).charAt(col)=='Q') return false;
        }

        /* 检查右上方是否有皇后互相冲突 */
        for(int i=row-1,j=col+1;i>=0&&j<n;i--,j++) {
            if(board.get(i).charAt(j)=='Q') return false;
        }

        /* 检查左上方是否有皇后互相冲突 */
        for(int i=row-1,j=col-1;i>=0&&j>=0;i--,j--) {
            if(board.get(i).charAt(j)=='Q') return false;
        }

        return true;
    }
}


在这里插入图片描述
该题目即在上边题目的代码上做出简单变动即可ac,就是不用在记录路径了,定义变量res记录符合规则的情况数,如果row到达底层,就让res++,最后返回res即可。

代码实现:

class Solution {
    int res=0;
    public int totalNQueens(int n) {
        List<String> board=new ArrayList<>();
        for(int i=0;i<n;i++) {
            StringBuffer sb=new StringBuffer();
            for(int j=0;j<n;j++) {
                sb.append(".");
            }
            board.add(sb.toString());
        }
        backtrack(board,0);
        return res;
    }
    void backtrack(List<String> board,int row) {
        if(row==board.size()) {
            res++;
            return;
        }
        int n=board.get(row).length();
        for(int col=0;col<n;col++) {
            if(!isValid(board,row,col)) continue;
            StringBuffer sb=new StringBuffer(board.get(row));
            sb.setCharAt(col,'Q');
            board.set(row,sb.toString());
            backtrack(board,row+1);
            sb.setCharAt(col,'.');
            board.set(row,sb.toString());
        }
    }
    boolean isValid(List<String> board,int row,int col) {
        int n=board.size();
        for(int i=0;i<row;i++) {
            if(board.get(i).charAt(col)=='Q') return false;
        }
        for(int i=row-1,j=col+1;i>=0&&j<n;i--,j++) {
            if(board.get(i).charAt(j)=='Q') return false;
        }
        for(int i=row-1,j=col-1;i>=0&&j>=0;i--,j--) {
            if(board.get(i).charAt(j)=='Q') return false;
        }
        return true;
    }
}

⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁

请添加图片描述

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

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

相关文章

【MySQL入门指南】主键与唯一键的使用与区别

文章目录 一、主键1.基本语法2.使用案例 二、唯一键1.基本语法2.使用案例 一、主键 1.基本语法 -- 方式一 create table t5(id int primary key, ……); -- 设置id字段主键-- 方式二 create table t5(id int primary key,……primary key(id, ……); -- 每个表只能有一个主键…

商城订单模块实战 - 分库分表实战及海量数据处理

商城订单服务的实现 数据量 在设计系统&#xff0c;我们预估订单的数量每个月订单2000W&#xff0c;一年的订单数可达2.4亿。而每条订单的大小大致为1KB&#xff0c;按照我们在MySQL中学习到的知识&#xff0c;为了让B树的高度控制在一定范围&#xff0c;保证查询的性能&…

归一化层(BatchNorm、LayerNorm、InstanceNorm、GroupNorm)

参考博客 BatchNormalization、LayerNormalization、InstanceNorm、GroupNorm、SwitchableNorm总结 PyTorch学习之归一化层&#xff08;BatchNorm、LayerNorm、InstanceNorm、GroupNorm&#xff09; BN&#xff0c;LN&#xff0c;IN&#xff0c;GN从学术化上解释差异&#xf…

前端常见报错问题处理及技术点收集

一、报错问题收集 1、页面停留半小时左右不动卡死报错问题 Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: http://10.233.54.161/assets/index.f8110bbc.js Promise.then (async) E main.c19f562f.js:39 f main.c19f562f.js:39 z.onClick…

Chatgpt聊天机器人系统开发

智能聊天ChatGPT的主要功能包括&#xff1a; 对话生成&#xff1a;生成连贯、自然的对话回复&#xff0c;与用户进行自然而流畅的对话。 意图识别&#xff1a;识别用户的意图和需求&#xff0c;并提供相应的回复或建议。 语义理解&#xff1a;理解用户的语言表达&a…

网络设备正常运行时间监控

什么是正常运行时间监控 正常运行时间是衡量服务器或任何网络组件对其最终用户的可用性的指标。定期检查网络设备可用性的过程称为正常运行时间监控。正常运行时间监控有助于确保所有组件保持正常运行&#xff0c;而不会停机。 正常运行时间监控是关键的网络监控功能&#xf…

Docker基础知识全解析

​ Docker是一个开源的容器化平台&#xff0c;可以让开发者在容器中构建、打包、运行和发布应用程序&#xff0c;从而实现应用程序的快速部署和可移植性。Docker将应用程序和依赖项打包在一个轻量级的可移植容器中&#xff0c;这个容器可以在任何平台上运行&#xff0c;不会受到…

Java 创建线程池的三种方式

一、 Java 创建线程池主要有以下三种方式 1. 默认线程池 ForkJoinPool 2. 通过调用执行器 Executors中的静态方法 3. 通过 ThreadPoolExector import java.util.concurrent.*;// 自定义线程工厂 class MyThreadFactory implements ThreadFactory {Override//ThreadFactory 主要…

从零开始学习Linux运维,成为IT领域翘楚(一)

文章目录 &#x1f525;Linux概述&#x1f525;Linux下载安装&#x1f525;Linux三种网络配置&#x1f525;Linux 远程登录 &#x1f525;Linux概述 Linux内核最初只是由芬兰人林纳斯托瓦兹1991年在赫尔辛基大学上学时出于个人爱好而编写的。 Linux特点 首先Linux作为自由软件…

递归实现指数型枚举

77. 组合 方法&#xff1a;递归 class Solution { private:vector<vector<int>> res;vector<int> path;void solve(int n, int k, int idx) {if (path.size() k) {res.push_back(path);return ;}for (int i idx; i < n - (k-path.size()) 1; i) {pat…

java 自定义Annotation注解

目录 1.声明注解 注解声明为interface&#xff08;注&#xff1a;这与interface接口没有任何关系&#xff09; 内部定义成员通常用value表示 使用 可以指定成员的默认值&#xff0c;使用default定义 介绍 2.JDK中的元注解 Retention&#xff1a; Target&#xff1a; …

用于高负载多站点网络的 WordPress Multisite Cron

在易服客建站平台创建免费网站 500M免费空间&#xff0c;可升级为10GB电子商务网站 创建免费网站 用于高负载多站点网络的 WordPress Multisite Cron 发布于 2023年3月18日 你也许知道WordPress 内置 CRON 的工作方式与传统 CRON 不同。 它不是在指定时间触发&#xff0c…

辨析 变更请求、批准的变更请求、实施批准的变更请求

变更请求、批准的变更请求、实施批准的变更请求辨析 辨析各种变更请求&#xff0c;不服来辨。 变更请求 定义&#xff1a;对正规受控的文件或计划(范围、进度、成本、政策、过程、计划或程序)等的变更&#xff0c;以反映修改或增加的意见或内容 根据变更请求的工作内容可将变…

python-使用Qchart总结3-绘制曲线图

1.将画好的图表关联 解释说明图 2.新建一个文件画曲线图&#xff0c;并关联到UI的py文件上&#xff0c;上代码 import sys from PyQt5.Qt import * from PyQt5.QtChart import QChartView, QChart, QValueAxis, QSplineSeries from PyQt5.QtGui import QPainter, QColor, QFon…

PHP实现使用foreach、for等语句实现数组遍历的功能举例

目录 前言 一、什么是数组 二、遍历数组for语句案例 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 三、输出数组的键名和值,foreach语句案例 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 前言 1.若有选择&#xff0c;可实现…

二十三种设计模式第二篇--工厂模式

上篇我们了解了6条设计模式的准则&#xff0c;我相信如果你想了解设计模式&#xff0c;那么你迈出的第一步&#xff0c;我会将上一篇文档里边的6大准则进行一篇有关的代码展示&#xff0c;当然这是题外话了&#xff0c;本篇我们将重点围绕工厂模式进行讲解&#xff0c;天哪&…

Shell+VCS学习1

Shell脚本常见问题 mkdir rmdir rm mkdir 创建文件夹 mkdir -p filename-p 确保目录名称存在&#xff0c;不存在的就建一个。 mkdir -p runoob2/test若 runoob2 目录原本不存在&#xff0c;则建立一个。&#xff08;注&#xff1a;本例若不加 -p 参数&#xff0c;且原本 ru…

【C++】反向迭代器的实现

文章目录 1.迭代器的分类2.反向迭代器的使用3.反向迭代器的模拟实现4.list类的反向迭代器实现 1.迭代器的分类 我们随便打开一个容器&#xff0c;看迭代器相关的接口&#xff0c;都可以发现&#xff0c;支持迭代器的容器&#xff0c;其迭代器有以下几类 正向迭代器const正向迭…

软件测试必备的Linux知识(一)

1. Linux 概述 1.1 测试人员为什么学习linux 对于软件测试人员来说&#xff0c;我们测试的任何产品都是基于操作系统。比如我们每天都在使用的QQ软件&#xff0c;它有windows、ios、Android、Mac OS等版本&#xff0c;需要把QQ安装在各个平台上&#xff0c;才能进行相应的测试…

03 KVM虚拟机镜像制作

文章目录 03 KVM虚拟机镜像制作3.1 概述3.2 制作镜像3.2.1 使用root用户安装qemu-img软件包3.2.2 使用qemu-img工具的创建镜像文件 3.3 修改镜像磁盘空间大小3.3.1 查询当前虚拟机镜像磁盘空间大小3.3.2 修改镜像磁盘空间大小3.3.3 查询修改后的镜像磁盘空间大小 03 KVM虚拟机镜…