数据结构与算法-- 八皇后问题(多种实现方案)

article/2025/9/15 9:53:55

八皇后问题解法一(排列筛选法)

  • 本篇我们承接上一篇中的思想,想到了一个经典的算法题,八皇后问题:
  • 题目:在8*8的国际象棋上摆放8个皇后,使得其互相不能攻击,即任意两个换后不能在同一行,同一列,或者同一对角线上。如下图中所示,就是一个符合预期的摆放方式,问总共有多少中摆放方式。

在这里插入图片描述

  • 上图中的数字代表此处放置一个皇后,并且从上到下依次是0~7 总共8个。

  • 分析:

    • 由于8个皇后任意两个不能处在同一行,那么肯定每个皇后占据一行。
    • 有上图看,我们必然可以用数组来标识myQueen[8] 数组,数组中i 个数字表示位于第i 行的皇后
    • 例如以上图为案例可以用数组 myQueen[8] = {4,2,0,5,7,1,3,6} 来标识这种摆放的可能,
      • 就是我们直接用数组下标当成皇后所在的列
      • 用数组的下标对应的值当成皇后所在的行
    • 那么现在我们的思路就是求出 myQueen数组的全排列,接着在筛选出符合要求的
    • 符合要求的情况:
      • 我们依据以上思路得到的全排列,天然就不会再同一行,也不会在同一列
      • 只需要筛选左右对角线上是否有其他数据,那么只需要对比下标 i 和 j的差值与 i和j 下标对应的value的差值是否相等
      • i - j == myQueen[i] -myQueen[j] || j-i == myQueen[i] - myQueen[j]
      • 以上判断可以用具体案例来验证此处略。
  • 依据如上分析有如下代码:

/***  八皇后问题* @author liaojiamin* @Date:Created in 15:03 2021/5/26*/
public class EightQueen {private static final List<Integer[]> myQueen = new ArrayList<>();public static void main(String[] args) {Integer[] eight = {0,1,2,3,4,5,6,7};getEightQueenPerMutain(eight, 0);List<Integer[]> realQueen = checkQueen(myQueen);for (Integer[] integers : realQueen) {for (int i = 0; i < integers.length; i++) {System.out.print(integers[i]+",");}System.out.println();}}/*** 检查所有可能的八皇后排列,筛选出符合条件的:* i-j == integer[i] - integer[j] || j-i = integer[i]-integer[j] 情况的都是统一对角的* */public static List<Integer[]> checkQueen(List<Integer[]> myQueen){List<Integer[]> realEightQueen = new ArrayList<>();for (Integer[] integers : myQueen) {boolean isCheck = true;for (int i = 0; i < integers.length; i++) {if(!isCheck){break;}for (int j = i+1; j<integers.length;j++){if((i-j) == (integers[i] - integers[j])|| (j-i) == (integers[i] - integers[j])){isCheck = false;break;}}}if(isCheck){realEightQueen.add(integers);}}return realEightQueen;}/*** 八皇后问题排列解决方案* */public static void getEightQueenPerMutain(Integer[] eight, Integer start){if(eight == null || eight.length <= 1 || start == eight.length-1){Integer[] newEight = new Integer[eight.length];for (int i = 0; i < eight.length; i++) {newEight[i] = eight[i];}myQueen.add(newEight);}for (int i = start; i <= eight.length-1; i++) {Integer temp = eight[start];eight[start] = eight[i];eight[i] = temp;getEightQueenPerMutain(eight, start+1);temp = eight[start];eight[start] = eight[i];eight[i] = temp;}}
}

八皇后问题解法二(动态规划)

  • 接上文中,依然可以用数组 myQueen[8] = {4,2,0,5,7,1,3,6} 来标识这种摆放的可能

  • 那么上文中将所有排列列举出来后在对排列集合进行筛选得到最终的八皇后集合

  • 类似的思路,我们用穷举法将数组myQueen中包含0 ~ 7 的所有数字的可能一一列举,并且实时筛选,这样我们可以一个步骤直接得到想要的集合

  • 经过如上分析有如下代码:

/***  八皇后问题* @author liaojiamin* @Date:Created in 15:03 2021/5/26*/
public class EightQueen {private static final List<Integer[]> myQueen = new ArrayList<>();public static void main(String[] args) {for (int i = 0; i < 8; i++) {Integer[] eight = new Integer[8];getEightQueen(eight, i);}for (Integer[] integers : myQueen) {for (int i = 0; i < integers.length; i++) {System.out.print(integers[i] + ",");}System.out.println();}System.out.println(myQueen.size());}/*** 穷举+筛选* 直接获取八皇后问题最终结果* */public static void getEightQueen(Integer[] queen, int start){for (int i = 0; i < 8; i++) {queen[start] = i;if(checkout(queen, start)){if(start == queen.length -1){Integer[] realQueen = new Integer[8];for (int i1 = 0; i1 < queen.length; i1++) {realQueen[i1] = queen[i1];}myQueen.add(realQueen);}else {getEightQueen(queen, start + 1);}}}}/**
* 校验是否符合八皇后问题规则
*/public static boolean checkout(Integer[] queen, Integer end){Set<Integer> checkRepeat = new HashSet<>();for (int i = 0; i <= end; i++) {if(checkRepeat.contains(queen[i])){return false;}checkRepeat.add(queen[i]);for(int j = i+1; j<= end; j++){if(queen[i] == null || queen[j] == null){return false;}if((i-j) == (queen[i]- queen[j]) || (j-i) == (queen[i] - queen[j])){return false;}}}return true;}
}

八皇后问题解法三(回朔递归)

  • 依然可以用数组 myQueen[8] = {4,2,0,5,7,1,3,6} 来标识这种摆放的可能
  • 首先将第一个皇后放入第一列位置
  • 接着将第二个皇后分别放入第二三四等之后的位置,每次分别去检查放入的位置是否与之前的位置中放入的皇后是否在同一列,同一对角线
  • 接着依次对地三个,第四个,直到第八个皇后重复第二步骤,找出每个皇后所有合法的位置
  • 方法三 与方法二的实现方式非常类似,区别在于筛选,方法三种的筛选只需要对比最后一个元素与之前的元素是否冲突,在方法三中是每个皇后的位置依次检查,当进行到第5个皇后的时候,能保证前4个皇后的位置都是合法的,无需在检查之前的。
  • 具体实现方案如下,方法三实现方案更好理解。
/*** 八皇后问题** @author liaojiamin* @Date:Created in 15:03 2021/5/26*/
public class EightQueen {private static final List<Integer[]> myQueen = new ArrayList<>();public static void main(String[] args) {getEightQueenRetro();print();}public static void print() {for (Integer[] integers : myQueen) {for (int i = 0; i < integers.length; i++) {System.out.print(integers[i] + ",");}System.out.println();}System.out.println(myQueen.size());}/*** 回朔递归*/public static void getEightQueenRetro() {Integer[] eight = new Integer[8];check(eight, 0);}//递归回朔便利每一种可能public static void check(Integer[] eight, int n) {if (n == eight.length) {myQueen.add(Arrays.copyOf(eight, eight.length));return;}for (int i = 0; i < eight.length; i++) {eight[n] = i;if(judge(eight, n)){check(eight, n+1);}}}//筛选public static boolean judge(Integer[] eight, int n){for(int i=0;i<n;i++){if(eight[i] == eight[n] || Math.abs(n-i) == Math.abs(eight[n] - eight[i])){return false;}}return true;}
}

总结

  • 以上两个方法的整体思路类似,当题目需要我们按照一定规则摆放若干个数字的时候,我们可以先求出这些数字的所有可能,然后在一一判断每个组合是否满足题目给定的要求。
  • 方法一利用上一篇中的排列的算法得出所有排列可能,接着筛选,时间复杂度O( n3 )
  • 方法二 利用递归穷举方法得出所有数字的可能,并同时筛选,时间复杂度也是O(n3)
  • 两种算法的时间复杂度都并非很优,如有更好的算法思想,求在评论指出

上一篇:数据结构与算法–字符串的排列组合问题
下一篇:数据结构与算法-- 数组中出现次数超过一半的数字(时间复杂度的讨论)


http://chatgpt.dhexx.cn/article/cY8jI47v.shtml

相关文章

C语言开方问题求助

求助&#xff1a; 使用的是Devcpp进行的编程尝试 在写程序的时候遇到了个问题&#xff0c;运行后结果显示为1#j 后来在朋友的帮助下改正了程序&#xff0c;解决了错误。之后的话&#xff0c;我按照我朋友的改正过的程序自己再重新打了一遍&#xff0c;结果运行后的结果还是显示…

C语言笔记

目录 基础... 3 基本类型数据... 5 定义变量... 6 进制... 6 ASCII&#xff08;阿斯克码&#xff09;... 7 输入输出... 8 printf&#xff08;&#xff09;将变量的内容输出到显示器上... 8 scanf &#xff08;&#xff09;[通过键盘将数据输入到变量中] 9 运算符... …

c语言 大数开方,大数加法之C语言函数法(只有正数版)

由于某些原因&#xff0c;我于今天2017-4-19将我的博文搬到博客园了&#xff0c;以后我就在这里扎根了。 之前想过在博客写文章方便日后复习&#xff0c;但一直未能实现&#xff0c;所以&#xff0c;现在这篇是我个人人生中第一篇博客&#xff0c;所以写博客完全没经验&#xf…

开平方的快速算法(C代码)

目录&#xff1a; 一、牛顿迭代法 二、采用移位、加减法、判断和循环实现开平方 三、效率远高于牛顿迭代法开平方法 1、原理 2、实现代码 四、卡马克快速开平方算法(推荐) 1、C-Free模拟验证卡马克开平方 2、移植到实际的项目 3、卡马克快速开平方的由来 1&#xff0…

windows 区域截屏以及延迟截屏

提起在Windows&#xff0c; 我们都会用到截屏功能&#xff0c;今天论述一下window 10系统自带的截图应用Snipping Tool 打开Snipping Tool 找到任务栏下的放大镜图标&#xff0c;点击 在下方输入snipping&#xff0c;会在左侧找到截图软件Snipping Tool&#xff0c;点击可进入…

小米手机解决此区域不可截屏

小米手机解决此区域不可截屏 无意中暂停视频弹出消息&#xff0c;想试试可不可以截屏竟然可以截屏&#xff0c;但是视频一播放就截屏不了了&#xff0c;录屏也是&#xff0c;直接变黑或者是直接提示弹窗&#xff0c;嘻嘻嘻嘻小米bug还是有好处滴

浏览器网页截屏实用小技巧

浏览器开发者工具中自带的截屏太方便了&#xff01; 打开开发者工具&#xff0c;输入 ctrl shift P 快捷键&#xff0c;输入screenshot&#xff0c;出现了四个选项&#xff0c;分别是&#xff1a; 1.area screenshot - 区域截图 2.full size screenshot - 对浏览器所有内容…

如何利用计算机截屏快捷键,电脑怎么截图 电脑选区域截图怎么截 电脑截图快捷键是什么...

电脑怎么截图 按照操作上从易到难的顺序&#xff0c;给你推荐五种截屏方式 &#xff1a; 第一种&#xff1a;Ctrl PrScrn 使用这个组合键截屏&#xff0c;获得的是整个屏幕的图片; 第二种&#xff1a;Alt PrScrn 这个组合键截屏&#xff0c;获得的结果是 当前窗口的图片; 第三…

iOS 截屏指定区域

转自&#xff1a;链接&#xff1a;https://www.jianshu.com/p/39db0fa66c0e 指定截屏代码实现 全屏截图效果 全屏截图效果 指定区域截屏效果 指定区域截屏效果 这里先上代码&#xff0c;代码后面有相关方法的解释第一种方法代码下载 /**创建一个基于位图的上下文&#xff0…

windows如何截屏

截屏是我们平时工作或记录常用的操作&#xff0c;不过有人不知道怎么用系统截屏&#xff0c;今天&#xff0c;小编带来了系统的几种截屏&#xff0c;让我们来看看吧&#xff01; 一、快捷键截图 1. Win shift S&#xff1a;可以选择截图区域的大小&#xff0c;CtrlV粘贴在w…

Android 任意区域截屏

1、全屏截图 Android其实可以做到任意区域截屏&#xff0c;不过我们先来看看整个屏幕截图代码&#xff0c;相信大家很熟悉&#xff0c;代码如下 View decorView activity.getWindow().getDecorView(); decorView.setDrawingCacheEnabled(true); view.buildDrawingCache(); /…

snipaste 固定位置截屏

原文参考&#xff1a; snipaste怎么固定位置截图&#xff0c;如何统一大小截图 一、电脑点击【snipaste】&#xff0c;或者点击键盘的“F1”。 二、在图片&#xff0c;根据自己的需求&#xff0c;画出截图的位置和大小&#xff0c;比如&#xff1a;本篇是500*296。 三、点击右…

JavaScript实现浏览器特定区域截屏和下载功能

JavaScript实现浏览器特定区域截屏功能 需求介绍尝试一&#xff1a;使用Jtopo.js自带的保存图片方法&#xff08;不能对资源进行下载&#xff09;尝试二&#xff1a;对saveImageInfo进行改写&#xff08;功能能用&#xff0c;但是会因为跨域问题污染canvas&#xff09;&#xf…

Unity中的截图方法(包括全屏截图、区域截图、Camera截图和摄像头截图)

Unity中的截图方法&#xff08;包括全屏截图、区域截图、Camera截图和摄像头截图&#xff09; Application.CaptureScreenshotScreenCapture Texture2D.ReadPixels视口截图RenderTexture&#xff08;Camera截图&#xff09;WebCamTexture&#xff08;摄像头截图、照相&#xff…

实现区域截图功能

利用QQ或微信自带的截图功能实现区域截图。 在腾讯安装目录下找到PrScrn.dll&#xff0c;并将它放在需要的位置&#xff0c; 将D:/PrScrn.dll修改为你的目录。 如果在maya里面直接使用该代码 import os,subprocess from PySide2.QtWidgets import QApplication clipboard …

小米手机怎么截屏?小米手机区域截屏

小米手机怎么截屏&#xff1f;手机的截屏其实都是差不多的&#xff0c;基本上都是三指向下滑动而达到截屏效果的&#xff0c;但基本都是全屏截图。小米手机区域截屏怎么做&#xff1f;如果想要做到任意位置的那种区域块截屏的话&#xff0c;该怎么做&#xff1f;下面就来看看吧…

浏览器截图方法(长截图、node截图、指定区域截图)

1.打开需要截屏的页面&#xff0c;按键盘上的F2&#xff08;或者CtrlShiftI&#xff09;打开浏览器控制台 2.CtrlshiftP进入搜索框&#xff0c;输入“screen”: 这里有四种截图模式&#xff0c;点击需要的截图方式即可截取图片。

Windows关闭指定端口命令

假设要关闭端口号为3003&#xff0c;使用下面的命令&#xff0c;查出此端口号对应的PID netstat -ano|findstr 3003 上图红框内的 22876 就是3003端口对应的PID&#xff0c;再使用下面的命令就可以关闭这个端口了 taskkill /PID 22876 /F

Linux关闭端口

netstat -anp | grep xxx //查看端口是否被占用kill -9 10762 //即可关闭端口

linux开放端口和关闭端口

centos6&#xff1a; 关闭防火墙:service iptables stop 开启防火墙:service iptables start 防火墙状态:service iptables status 永久关闭:chkconfig iptables off 永久开启:chkconfig iptables on 方法一(命令): 1. 开放端口命令&#xff1a; /sbin/iptables -I INPUT…