JAVA中的回溯算法解空间树,八皇后问题以及骑士游历问题超详解

news2024/9/17 9:11:27

1.回溯算法的概念

回溯算法顾名思义就是有回溯的算法

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。

回溯算法的核心思想是:

  1. 问题分解:将原问题分解成若干个规模较小的子问题。
  2. 候选解的生成:从根节点开始,逐层生成候选解。
  3. 活节点:当前正在扩展的节点称为活节点。
  4. 死节点:当前扩展的节点如果已经确定不能得到问题的解,则该节点称为死节点。
  5. 剪枝:根据问题的限制条件,对已经确定的死节点进行剪枝。

2.回溯算法解空间树的问题

 典型案例:

集合求幂集的解空间树

意思就是一个集合s所有的子集的集合

问题:求集合S={1,2,3}的所有子集,依次选择每个元素的过程就是一棵树,如图所示

在树中,由从根节点到叶子结点的每条路径上的权值组成三元组(0,0,0)~(1,1,1)都是解,如(0,0,1)表示子集{B,C},共2^{3}=8个解;因此,该树被称为解空间树,高度为4。

为什么帮助理解,我们先看普通的递归方法是什么实现的

下面是迭代方法代码实现:

import java.util.ArrayList;
import java.util.List;

public class PowerSet {
    public static void main(String[] args) {
        List<String> originalSet =new ArrayList<>();
        originalSet.add("A");
        originalSet.add("B");
        originalSet.add("C");
        //这里又嵌套了一个list是为了使每一个元素都变成list
        //适用于多种算法和数据处理任务
        List<List<String>> powerSet =getPowerSet(originalSet);

        for (List<String> subset:powerSet){
            System.out.println(subset);
        }
   }

    private static List<List<String>> getPowerSet(List<String> originalSet) {
        List<List<String>> powerSet =new ArrayList<>();
        if (originalSet==null||originalSet.isEmpty()){
            return powerSet;
        }
        //添加空集
        powerSet.add(new ArrayList<>());

        for (String element:originalSet){
            int size =powerSet.size();
            for (int i =0;i<size;i++){
                List<String> subset =new ArrayList<>(powerSet.get(i));
                subset.add(element);
                powerSet.add(subset);
            }
        }
        return powerSet;

    }

现在让我们来分析一下这个过程:

假设第一次迭代中,我们处理元素“A”:

  • 初始时,powerSet只包含一个空集[ ]。
  • 我们复制这个空集,然后添加“A”,得到["A"]。
  • 将["A"]添加到powerSet,现在powerSet是[ ],["A"]。

在二次迭代中处理“B”:

  • 现在powerSet有两个子集:[ ]和["A"]。
  • 我们首先复制空集,然后添加“B”,得到["B"]。
  • 然后复制[“A”],然后添加“B”,得到["A","B"]。
  • 然后将这两个新的子集添加到powerSet,现在powerSet有[ ],["A"],["B"],["A","B"]

然后三次迭代重复这个过程就可以了。

下面是回溯方法的代码:

public class PowerSetWithBacktracking {
    public static void main(String[] args) {
        List<String> originalSet = new ArrayList<>();
        originalSet.add("A");
        originalSet.add("B");
        originalSet.add("C");

        List<List<String>> powerSet = new ArrayList<>();
        backtrack(0, originalSet, new ArrayList<>(), powerSet);

        for (List<String> subset : powerSet) {
            System.out.println(subset);
        }
    }

    private static void backtrack(int start, List<String> originalSet, List<String> currentSubset, List<List<String>> powerSet) {
        //将当前子集的副本添加到幂集powerSet中,这里使用new ArrayList<>()来复制当前的子集。
        powerSet.add(new ArrayList<>(currentSubset));
        //遍历集合中的每个元素,从start开始
        for (int i =start;i<originalSet.size();i++){
            //做出选择:包含当前元素
            currentSubset.add(originalSet.get(i));
            //递归调用:移动到下一个元素
            backtrack(i+1,originalSet,currentSubset,powerSet);
            //撤销选择:回溯
            currentSubset.remove(currentSubset.size()-1);

    }
}
}

分析过程:

步骤startcurrentSubsetpowerSet
第一次0[ ][[ ]]
第二次0["a"][[ ]]
第三次1["a","b"][[ ]]
第四次2["a","b","c"][[ ]]
第五次3["a","b","c"][[" "],["a","b","c"],["a","b"],["a"]]
第六次2["a","b"][[" "],["a","b","c"],["a","b"],["a"]]
第七次1["a"][[" "],["a","b","c"],["a","b"],["a"]]
第八次2["a","c"][[" "],["a","b","c"],["a","b"],["a"]]
第九次2["a","c"][[" "],["a","b","c"],["a","b"],["a"],["a","c"]]
第十次1["a"][[" "],["a","b","c"],["a","b"],["a"],["a","c"]]
第十一次1[ ][[" "],["a","b","c"],["a","b"],["a"],["a","c"]]
第十二次1["b"][[" "],["a","b","c"],["a","b"],["a"],["a","c"],["b"]]

 接下来依次推导就行了。整体的思路就是不断递归下一个元素,然后从currentSubset中移除最后添加的元素,进行回溯,准备处理下一个元素。

3.八皇后问题

八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上吗,问有多少种摆法。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当n = 1或n ≥ 4时问题有解

public class EightQueens {
    private static final int n =8;
    private static int[] queens;//存储皇后的位置
    private static int count =0;//解的计数

    public static void main(String[] args){
        queens=new int[n];
        for (int i =0;i<n;i++){
            queens[i]=-1;//初始化都为-1,表示都没有放皇后
        }
        placeQueens(0);//从第0行开始放置皇后
        System.out.println("");
    }

    private static void placeQueens(int row) {
        if (row==n){
            count++;
            printBoard();
            return;
        }
        for (int col =0;col<n;col++){
            if (isSafe(row,col)){
                queens[row]=col;//在当前位置放置皇后
                placeQueens((row+1));//进入下一行
                queens[row]=-1;//回溯,撤销当前行的皇后放置的位置
            }
        }

    }

    private static boolean isSafe(int row, int col) {
        //检查列是否有冲突
        for (int i =0;i<row;i++ ){
            if (queens[i]==col){
                return false;
            }
        }
        //检查左上对角线是否有冲突
        for (int i =row-1,j=col+1;i>=0&&j>=0;i--,j--){
            if (queens[i]==j){
                return false;
            }
        }
        //检查右下对角线是否有冲突
        for (int i =row-1,j=col+1;i>=0&&j<n;i--,j++){
            if (queens[i]==j){
                return false;
            }
        }
        return true;


    }

    private static void printBoard() {
        System.out.println("第"+count+"种解法");
        for(int i =0;i<n;i++){
            for (int j=0;j<n;j++){
                if (queens[i]==j){
                    System.out.println("Q");
                }else{
                    System.out.println(". ");
                }
            }
            System.out.println();
        }
        System.out.println();
    }
}

过程分析:

回溯发生在两种情况下:

  1. 当前行是最后一行(row==n-1),并且已经尝试了所有可能的列,并且已经找到了一个解决方案,递归将终止,开始回溯
  2. 在递归过程中,当前行不是最后一行,但在下一行无法找到任何安全位置放置皇后。

 4.骑士游历问题

骑士游历问题是指,在国际象棋的棋盘(8行*8列)上,一个马要遍历棋盘,即走到棋盘上的每一格,并且每隔只到达一次。 设码在棋盘的某一位置(x,y)上,按照“走马日”的规则,下一步有8个方向走,如图所示。 若给定起始位置(x0,y0),使用站和队列探索出一条马遍历棋盘的路径。

 算法步骤:

  • 定义棋盘大小和骑士的可能移动的方式。
  • 创建一个二维数组来表示棋盘,初始值设为-1表示未访问。
  •  编写一个辅助函数来检查当前位置是否合法。
  • 编写一个递归函数来尝试所有可能的移动,使用回溯法来寻找解。
  • 在主函数中调用递归函数,并从指定的起始点开始。


public class KnightTour{
  private static final int N =8;
  private static final int[][] moves={{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};

    public static void main(String[] args) {
        int[][] board =new int[N][N];
        for (int i =0;i<N;i++){
            for (int j =0;j<N;j++){
                board[i][j]=-1;
            }
        }
        //起始点
        int x =0,y =0;
        board[x][y]=0;

        if (!solveKTUtil(board,x,y,1)){
            System.out.println("此解决方案不存在");
        }else{
            printSolution(board);
        }
    }

    private static boolean solveKTUtil(int[][] board, int x, int y, int moveCount) {
        if (moveCount==N*N){
            return true;
        }
        for (int i =0;i<8;i++){
            int nextX =x+moves[i][0];
            int nextY =y+moves[i][1];

            if (isSafe(board,nextX,nextY)){
                board[nextX][nextY]=moveCount;
                if (solveKTUtil(board,nextX,nextY,moveCount+1)){
                    return true;
                }else{
                    board[nextX][nextY]=-1;//回溯
                }
            }
        }
        return false;

    }

    private static boolean isSafe(int[][] board, int X, int Y) {
        return X>=0&&Y>=0&&X<N&&Y<N&&board[X][Y]==-1;
    }
    private static void printSolution(int[][] board){
        for (int i =0;i<N;i++){
            for(int j =0;j<N;j++){
                System.out.print(board[i][j]+" ");
            }
            System.out.println();
        }
    }
}

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

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

相关文章

Python 神器:wxauto 库——解锁微信自动化的无限可能

&#x1f4dd;个人主页&#x1f339;&#xff1a;誓则盟约 ⏩收录专栏⏪&#xff1a;机器学习 &#x1f921;往期回顾&#x1f921;&#xff1a;“探索机器学习的多面世界&#xff1a;从理论到应用与未来展望” &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f…

短视频矩阵:批量发布的秘密揭秘

在数字化时代&#xff0c;短视频已经成为一种广受欢迎的媒体形式。无论是用于品牌推广、产品营销还是个人创作&#xff0c;短视频都提供了一种直观、生动的方式来吸引观众的注意力。然而&#xff0c;有效地制作、管理和发布短视频对于许多创作者和企业来说是一个挑战。 为此&am…

【Django】报错‘staticfiles‘ is not a registered tag library

错误截图 错误原因总结 在django3.x版本中staticfiles被static替换了&#xff0c;所以这地方换位static即可完美运行 错误解决

旧衣回收小程序开发,提高回收效率,实现创收

随着人们生活水平的提高&#xff0c;对穿衣打扮也越来越重视&#xff0c;衣服更换频率逐渐增高&#xff0c;旧衣回收行业因此产生&#xff0c;并随着市场规模的扩大&#xff0c;拥有了完善的回收产业链&#xff0c; 旧衣回收行业的发展不仅能够让大众获得新的赚钱方式&#xf…

探索创意无限:精彩APP启动页设计案例汇总

启动页是应用启动时出现的过渡页面或动画&#xff0c;旨在提升用户体验、缓解用户焦虑&#xff0c;以及传递品牌或产品的人情味。根据应用性能&#xff0c;其停留时间可能从1秒到3秒不等。启动页同样是应用名片&#xff0c;需要展现品牌的独特个性。运用品牌颜色和 IP 形象能加…

C语言 指针和数组——指针数组的应用:命令行参数

目录 命令行参数 演示命令行参数与main函数形参间的关系 命令行参数  什么是 命令行参数&#xff08; Command Line Arguments &#xff09;&#xff1f;  GUI 界面之前&#xff0c;计算机的操作界面都是字符式的命令行界面 &#xff08; DOS 、 UNIX 、 Linux &…

215.Mit6.S081-实验三-page tables

在本实验室中&#xff0c;您将探索页表并对其进行修改&#xff0c;以简化将数据从用户空间复制到内核空间的函数。 一、实验准备 开始编码之前&#xff0c;请阅读xv6手册的第3章和相关文件&#xff1a; kernel/memlayout.h&#xff0c;它捕获了内存的布局。kernel/vm.c&…

什么是渲染:两种渲染类型、工作原理

如果您是网页设计师或数字艺术家&#xff0c;您可能熟悉渲染过程的概念。这是数字艺术中的重要步骤&#xff0c;帮助您将图形模型转换为最终结果。在本文中&#xff0c;您将了解数字艺术中的渲染是什么、它的工作原理以及它的类型。 一、什么是渲染? 渲染是使用计算机软件对数…

怎么样的主食冻干算好冻干?品质卓越、安全可靠的主食冻干分享

当前主食冻干市场产品质量参差不齐。一些品牌过于追求营养数据的堆砌和利润的增长&#xff0c;却忽视了猫咪健康饮食的基本原则&#xff0c;导致市场上出现了以肉粉冒充鲜肉、修改产品日期等不诚信行为。更令人担忧的是&#xff0c;部分产品未经过严格的第三方质量检测便上市销…

Python实现傅里叶级数可视化工具

Python实现傅里叶级数可视化工具 flyfish 有matlab实现&#xff0c;我没matlab&#xff0c;我有Python&#xff0c;所以我用Python实现。 整个工具的实现代码放在最后,界面使用PyQt5开发 起源 傅里叶级数&#xff08;Fourier Series&#xff09;由法国数学家和物理学家让-巴…

Apache网页优化(企业网站结构部署与优化)

本章结构 一、Apache网页优化 在使用 Apache 作为 Web 服务器的过程中&#xff0c;只有对 Apache 服务器进行适当的优化配置&#xff0c;才能让 Apache 发挥出更好的性能。反过来说&#xff0c;如果 Apache 的配置非常糟糕&#xff0c;Apache可能无法正常为我们服务。因此&…

链接报错undefined reference to + libc++和libstdc++

1 问题现象 subscribe(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) 描述&#xff1a;编译的时候&#xff0c;最后的链接中一直没成功 2 可能原因 2.1 链接时缺失了相关目标文件&#xff08;.o&#x…

Visual Studio 2022 安装及使用

一、下载及安装 VS 官网&#xff1a;Visual Studio: IDE and Code Editor for Software Developers and Teams 下载免费的社区版 得到一个.exe文件 右键安装 选择C开发&#xff0c;并修改安装位置 等待安装 点击启动 二、VS的使用 1.创建项目 打开VS&#xff0c;点击创建新项…

1招搞定maven打包空间不足问题

目录 一、工具应用问题 二 、使用效果 三、使用方法 四、练习手段 一、工具应用问题 使用maven的package功能打包失败&#xff0c;报错“Java heap space”错误。 二 、使用效果 修改IDEA中maven内存使用大小后&#xff0c;打包成功。 三、使用方法 点击菜单“File->Set…

openWrt(4) - uci

uci show 1) uci show - 查看所有配置文件列表 2)查看特定配置文件的详细信息&#xff1a; uci show network 我们以 network 为例 3&#xff09;查看特定配置项的详细信息&#xff1a; uci show network.wan 添加一个新的配置条目&#xff1a;uci add network interface …

Apifox报错404:网络错误,请检查网络,或者稍后再试的解决办法

详细报错如图&#xff1a; 解决办法&#xff1a; 1、检查 请求方法&#xff08;get&#xff0c;post&#xff09;是否正确&#xff0c;请求的URL是否正确&#xff0c;如果不正确&#xff0c;修改后重新发起请求&#xff1b;如果都正确&#xff0c;看2 2、复制curl用postman来…

安防监控/视频汇聚平台EasyCVR设备录像回看请求播放时间和实际时间对不上,是什么原因?

安防监控EasyCVR视频汇聚平台可提供多协议&#xff08;RTSP/RTMP/国标GB28181/GAT1400/海康Ehome/大华/海康/宇视等SDK&#xff09;的设备接入、音视频采集、视频转码、处理、分发等服务&#xff0c;系统具备实时监控、云端录像、回看、告警、平台级联以及多视频流格式分发等视…

当海底捞开始“抠”服务,火锅还能好吗

在胖东来因好服务在互联网上被献上诸多赞誉时&#xff0c;另一家曾因服务被赞为“学不会”的海底捞&#xff0c;却在经受质疑。 最近&#xff0c;社交媒体上&#xff0c;海底捞的消息有点儿多。先是5月30日&#xff0c;#海底捞玩具大人不能随便拿了#登上热搜&#xff0c;后是6…

开发TEE的踩坑之开发TEE

系统&#xff1a;Ubuntu20.04&#xff08;双系统&#xff0c;非虚拟机&#xff09; 一、前置说明1、TEE平台的选择2、机器间的通信方式3、程序和数据集的示例4、结果文件的解密 二、服务器部署三、客户端部署四、工程应用 本系列为笔者开发TEE&#xff08;Trusted Execution En…

华为USG6000V防火墙v1

目录 一、实验拓扑图 二、要求 三、IP地址规划 四、实验配置 1&#x1f923;防火墙FW1web服务配置 2.网络配置 要求1&#xff1a;DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00-18:00)可以访问&#xff0c;生产区的设备全天可以访问 要求2&#xff1a;生产区不…