【数据结构与算法】第十九篇:回溯,剪枝,N皇后问题

news2025/3/3 4:24:07

知识导航

  • 一、回溯思想概述
  • 二、八皇后问题引入
    • 八皇后问题的解决思路
      • (1)思路一:暴力出奇迹
      • (2)思路二:根据题意减小暴力程度
      • (3)思路三:回溯法+剪枝
  • 三、四皇后问题
    • 八皇后问题
  • 四、N皇后的实现
    • 1.实现方法一:利用数组下标和元素形成行,列对应关系
    • 2.实现方法二:利用布尔数组及进行优化
    • 3.实现方法三:位运算进行进一步优化(仅针对N皇后)


一、回溯思想概述

🥳🥳 回溯可以理解为:通过选择不同的岔路口来通往目的地(找到想要的结果)
1.每一步都选择一条路出发,能进则进,不能进则退回上一步(回溯),换一条路再试
2. 树、图的深度优先搜索(DFS)、八皇后、走迷宫都是典型的回溯应用
在这里插入图片描述

二、八皇后问题引入

◼ 八皇后问题是一个古老而著名的问题
1.在8x8格的国际象棋上摆放八个皇后,使其不能互相攻击:任意两个皇后都不能处于同一行、同一列、同一斜线上
2.请问有多少种摆法?
在这里插入图片描述

八皇后问题的解决思路

(1)思路一:暴力出奇迹

从 64 个格子中选出任意 8 个格子摆放皇后,检查每一种摆法的可行性
一共 C648种摆法(大概是 4.4 ∗ 10 9 种摆法)

(2)思路二:根据题意减小暴力程度

在这里插入图片描述

(3)思路三:回溯法+剪枝

我们可以用回溯法和剪枝思想解决8皇后问题乃至N皇后问题。我们由简单到复杂,先对4皇后进行一个思想梳理,请看下文👇👇

三、四皇后问题

四皇后问题的回溯示意图
在这里插入图片描述
由图中不难看出 3 5 都发生了回溯,那么什么是剪枝思想呢?
在这里插入图片描述

如果第一次选的是0下标节点,那么和这个节点的斜对角节点1,和这个节点的同一列的节点0都不会再被选中,因为是按一行一行进行选择的所以行的限制不做考虑。这种越过一部分的节点不做选择的操作称为剪枝操作。

八皇后问题

由上面的四皇后我们可以推及到8皇后问题的操作
在这里插入图片描述在这里插入图片描述

和四皇后的方法一样一行一行进行选择,如果发现能放的节点数不够存储剩下皇后则回溯到上一个操作,重新选择节点(上面的图并不是完整的回溯过程。可以自己试着自己推一下)。

四、N皇后的实现

1.实现方法一:利用数组下标和元素形成行,列对应关系

成员变量

 /**
     * 第几行的第几列存放皇后
     * 下标为行号,存储的为列号
     */
    private int []cols;
    private int ways;

放置皇后


    //n->有多少皇后
    public void placeQueues(int n){
        if(n<1)return;//至少得有一个皇后
        cols=new int [n];
        place(0);
        System.out.println(n+"皇后共有"+ways+"种摆放方法");
    }

    /**
     * 从第几行存放皇后
     * @param row
     */
    public void place(int row){
        //一种情况已经放完
        if(row==cols.length)
        {
            ways++;
            printf();
            return;
        }

        //行号已经确定row,遍历列好
        for (int col = 0; col <cols.length; col++) {
            //isValid()实际是进行了剪枝操作
            if(isValid(row,col)){
                cols[row]=col;
                //放置下一行
                place(row+1);
            }

        }


    }

放置皇后的可行性判断

 public boolean isValid(int row,int col){
        for(int i=0;i<row;i++) {
            //行是一行一行往下的,所以这里不用进行的行的判断
            //判断列,相等说明同一列已经放了元素
            if (cols[i] == col) return false;
            //row-i/col-cols[i]==1
            //row-i必是正数,斜率
            if (row-i==Math.abs(col-cols[i])) return false;
        }
        return true;
    }

打印操作


    public void printf(){
        for (int row = 0; row < cols.length; row++) {
            for (int col = 0; col < cols.length; col++) {
                if (cols[row] == col) {
                    System.out.print("1 ");
                } else {
                    System.out.print("0 ");
                }
            }
            System.out.println();
        }
        System.out.println("------------------------------");
    }

2.实现方法二:利用布尔数组及进行优化

//N皇后问题-->成员变量优化
//在第一种写法时有一个缺点在进行isValid()每次都要进行for循环,效率很差
//这种写法的缺点是:不适用于太多皇后的安排,因为额外开辟了三个数组空间
public class NQueens1 {

    //下标是行号,元素是列号:保留这个数组-->方便显示(打印)皇后的具体位置
    private int [] queens;
    /**
     * 标记某一列是否有皇后
     * 下标为行号,存储的为列号
     * 左上--->右下:指的是对角线的指向
     */
    private boolean []cols;
    private boolean []leftTop;//标记一个方向的斜线是否有皇后(左上--->右下)
    private boolean []rightTop;//标记一个方向的斜线是否有皇后(右上--->左下)
    private int ways;

    //n->有多少皇后
    public void placeQueues(int n){
        if(n<1)return;//至少得有一个皇后
        queens=new int[n];
        cols=new boolean[n];
        leftTop=new boolean[(n<<1)-1];//同一个方向有2*n-1个斜线
        rightTop=new boolean[leftTop.length];
        place(0);
        System.out.println(n+"皇后共有"+ways+"种摆放方法");
    }

    /**
     * 从第几行存放皇后
     * @param row
     */
    public void place(int row){
        //一种情况已经放完
        if(row==cols.length)
        {
            ways++;
            show();
            return;
        }

        //行号已经确定row,遍历列好
        for (int col = 0; col <cols.length; col++) {
                if(cols[col])continue;//剪枝:这一列已经有皇后了
                int ltIndex=row-col+cols.length-1;
                if(leftTop[ltIndex])continue;
                int rtIndex=row+col;
                if(rightTop[rtIndex])continue;
                queens[row] = col;
                cols[col]=true;
                //放置下一行
                leftTop[ltIndex]=true;
                rightTop[rtIndex]=true;
                place(row+1);
                //到这里说明row+1行没有可以放的位置,药要进行回溯;重置
                //之前没有改是因为,新的设置会覆盖掉原来的设置
                cols[col]=false;
                //放置下一行
                leftTop[ltIndex]=false;
                rightTop[rtIndex]=false;

        }
    }
    void show() {
        for (int row = 0; row < cols.length; row++) {
            for (int col = 0; col < cols.length; col++) {
                if (queens[row] == col) {
                    System.out.print("1 ");
                } else {
                    System.out.print("0 ");
                }
            }
            System.out.println();
        }
        System.out.println("------------------------------");
    }
}

3.实现方法三:位运算进行进一步优化(仅针对N皇后)

package Test02;

/**
 * 什么情况适用于位运算优化?
 * 1、存在布尔数组
 * 2.数组的长度不是很长(最好能用现有数据结构来表示)
 */

public class eightQueens {
    //下标是行号,元素是列号:保留这个数组-->方便显示(打印)皇后的具体位置
    private int [] queens;
    /**
     * cols:将boolean类型用 0 1表示。通过转化成二进制位压缩为byte(8个比特位)类型
     * [true  false  false true.....]--->[1 0 0 1......]
     * leftTop rightTop有十五个元素,所以用short存储就足够了
     *
     */
    byte cols;
    short leftTop;//标记一个方向的斜线是否有皇后(左上--->右下)
    short rightTop;//标记一个方向的斜线是否有皇后(右上--->左下)
    private int ways;

    //n->有多少皇后
    public void placeQueues(){
        queens=new int[8];
        place(0);
        System.out.println(8+"皇后共有"+ways+"种摆放方法");
    }

    /**
     * 从第几行存放皇后
     * @param row
     */
    public void place(int row){
        //一种情况已经放完
        if(row==8)
        {
            ways++;
            show();
            return;
        }

        //行号已经确定row,遍历列好
        for (int col = 0; col < 8; col++) {
            int cv = 1 << col;
            if ((cols & cv) != 0) continue;

            int lv = 1 << (row - col + 7);
            if ((leftTop & lv) != 0) continue;

            int rv = 1 << (row + col);
            if ((rightTop & rv) != 0) continue;

            queens[row] = col;
            cols |= cv;
            leftTop |= lv;
            rightTop |= rv;
            place(row + 1);
            cols &= ~cv;
            leftTop &= ~lv;
            rightTop &= ~rv;
        }
    }
    void show() {
        for (int row = 0; row < 8; row++) {
            for (int col = 0; col < 8; col++) {
                if (queens[row] == col) {
                    System.out.print("1 ");
                } else {
                    System.out.print("0 ");
                }
            }
            System.out.println();
        }
        System.out.println("------------------------------");
    }


}

在这里插入图片描述

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

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

相关文章

程序员的自我修养第七章——动态链接 (上)

继续更新《程序员的自我修养》这个系列&#xff0c;主要是夏天没把它看完&#xff0c;补上遗憾。本篇来自书中第七章。 再说动态链接前&#xff0c;我们先阐明为什么要动态链接&#xff1a; 动态链接的产生来自静态链接的局限性。随着静态链接的发展&#xff0c;其限制也越来越…

十二、创建和管理表

文章目录一、基础知识1.1 一条数据存储的过程1.2 标识符命名规则1.3 数据类型及数据库操作二、创建表三、查看表结构3.1 使用 SHOW COLUMNS 语句查看3.2 使用 DESCRIBE 语句查看3.3 查看表详细结构语句 SHOW CREATE TABLE四、修改表结构4.1 添加新字段和修改字段定义4.2 修改字…

用户画像增量更新系列二

进行用户日志数据处理 原始日志数据 结果: 思路&#xff1a;按照user_id的行为一条条处理&#xff0c;根据用户的行为类型判别。 由于sqlDF每条数据可能会返回多条结果&#xff0c;我们可以使用rdd.flatMap函数或者yield 格式&#xff1a;["user_id", "action…

总结:计算机中字符串比较大小的规则

总结&#xff1a;计算机中字符串比较大小的规则一背景&#xff1a;二Unicode编码表&#xff1a;字符越靠后&#xff0c;对应的十进制值越大三单个字符之间比较规则&#xff1a;四案例演示&#xff1a;单个字符与单个字符之间比较大小1.前提&#xff1a;汉字“一”与汉字“万”&…

Elasticsearch:Elasticsearch percolate 查询

Elasticsearch 通常如何工作&#xff1f; 我们将文档索引到 Elasticsearch 中并对其运行查询以获得满足提供的搜索条件的文档。 我们构造一个匹配或术语查询作为输入&#xff0c;匹配查询的文档作为结果返回。 但这不是 percolate query 的情况..... 让我们看看这篇文章中的 p…

10.Java方法学习知识点大全

文章目录前言一、什么是方法1.什么是方法?2.实际开发中,什么时候用到方法?3.实际开发中,方法有什么好处?二、最简单的方法定义和调用1.方法的格式2.方法的调用3.看代码说结果4.为什么要有带参数的方法呢?三、带参数的方法定义和调用1.带参数的方法定义和调用2.形参和实参3.…

NuSphere PhpED Pro 19.5 Crack

PhpED是PHP&#xff08;PHP IDE&#xff09;&#xff0c;HTML&#xff0c;CSS&#xff0c;XML&#xff0c;SMARTY&#xff0c;XHTML等的I ntegated Development Environment。 高级代码编辑器、可靠的 dbg 调试器、高效的数据库连接客户端以及快速安全的部署能力的平衡组合使 P…

90. 注意力分数及代码实现

1. 注意力分数 2. 拓展到高维度 3. Additive Attention ps&#xff1a; 这种的好处是&#xff0c;key&#xff0c;value&#xff0c;query的长度可以不一样 4. Scaled Dot-Product Attention n个query&#xff0c;m个key-value 对最后的结果是n x m的矩阵&#xff0c;第i行就表…

LeetCode[684]冗余连接

难度&#xff1a;中等题目&#xff1a;树可以看成是一个连通且 无环 的 无向 图。给定往一棵 n个节点 (节点值 1&#xff5e;n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1到 n中间&#xff0c;且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n的二维数组…

Python 压缩 css 文件,第三方模块推荐

本篇博客为大家详细介绍一下如何在 Python 中压缩 CSS 文件。 正式开始前&#xff0c;需要准备一个未压缩过的 CSS 文件。 Python 压缩 csscsscompressor 库使用在 Flask 中压缩 css 文件cssmin 库的用法rcssmin 库的用法总结csscompressor 库使用 在 Python 中可以使用多种方…

CSS之浮动以及清除浮动的几种方式

一. 什么是 CSS Float&#xff08;浮动&#xff09; CSS 的 Float&#xff08;浮动&#xff09;&#xff0c;会使元素向左或向右移动&#xff0c;其周围的元素也会重新排列。 Float&#xff08;浮动&#xff09;&#xff0c;往往是用于图像&#xff0c;但它在布局时一样非常有…

【大数据管理】Java实现字典树TireTree

实现字典树&#xff0c;支持插入和删除&#xff0c;能够打印每一层的数据示例数据“SJ”, “SHJ”, “SGYY”,"HGL" ,将这些数据插入前缀树&#xff0c;打印树&#xff0c;修改SHZ为SHHZ 解题思路 Trie树即字典树&#xff0c;又称单词查找树或键树&#xff0c;是一…

Linux下进程控制详解

目录 一、进程创建 1.1 初识fork 1.2 函数返回值 1.3 写时拷贝技术 1.4 fork函数的使用场景 1.5 fork函数的失败原因 二、进程终止 2.1 进程退出场景 2.2 进程退出码 2.3 进程正常退出方法 2.3.1 exit函数 2.3.2 _exit函数 2.3.3 return方法 2.3.4 方法分析对比 …

【LINUX修行之路】——工具篇gcc/g++的使用和自动化构建工具make/makefile

学习范围&#xff1a;✔️LINUX ✔️ gcc/g✔️make/makefile作者 &#xff1a;蓝色学者 文章目录一、前言二、概念什么是gcc/g&#xff1f;什么是make/makefile&#xff1f;三、教程3.1gcc/g命令3.2make/makefile依赖关系依赖方法编写makefile文件四、资源一、前言 欢迎大家来…

谷粒学院——Day20【项目总结】

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

计算机组成原理实验-logisim实现自动售糖机

一.作业内容; 二.设计分析&#xff1a; 首先我们先确定输入和输出&#xff0c;根据题目的提示很明显可以看出因为每次可以投入10元或者5元硬币&#xff0c;当总钱数达到15元或者超过15元的时候&#xff0c;自动出糖&#xff0c;并且机器不找零&#xff0c;所以可以看出最大的钱…

基于 V2G 技术的电动汽车实时调度策略(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

第九层(2):STL之string类

文章目录前情回顾string类string类的本质string与char*的区别string类的特点string类的构造函数string类内的字符串追加函数string类内的字符串查找函数string类内的字符串替换函数string类内的字符串比较函数string类内的字符单个访问函数string类内的插入函数string类内的删除…

最小化最大值+拓扑排序要点+概率

今天嫖来的两道题&#xff1a; D.ScoreofaTreeD. Score of a TreeD.ScoreofaTree E.EdgeReverseE. Edge ReverseE.EdgeReverse DDD题是比较离谱的一道题&#xff0c;你在做的时候好像是dp&#xff0c;但是选择的情况太多了&#xff0c;其实对于每一个节点来说&#xff0c;除了叶…

fpga实操训练(fpga和cpu之间的配合)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 cpu和fpga之间,各有各的优势,cpu开发比较快捷,程序员比较好找;fpga对于基础运算效率高,但是找人不好找。实际产品的开发中,一般cpu负责需要接口定义和个性化定制的地方,而fp…