Detailed eight queens (.java .cpp .c) three kinds of codes (including the application data structure stack)

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/qq_40176716/article/details/85336869

This article from the upper part of the public No. programmer small gray

Eight queens problem is an old and well-known problems, is a typical example of backtracking algorithms. The problem is well-known nineteenth-century mathematician Gauss 1850: 8'8 grid on the chess board, placed eight Queen, a Queen's request was not able to "eat" any other queen, that is, any two Queens are not in the same row, same column or the same diagonal, solving how many pendulum method.

Gauss think there are 76 kinds of programs. 1854 in Berlin on different chess magazine published 40 kinds of different solutions, it was later concluded by Graph Theory, there are 92 kinds of pendulum method.

 

Queen chess can be horizontal, vertical, diagonal movement. How to place eight queens on the board of a 8X8 that any two queens are not on the same horizontal, vertical, diagonal direction ?

Let's take chestnuts, green plaid in the figure below is a queens on the board of the "blockade" and other Queens may not be placed in the grid:

Green plaid following figure is two queens on the board of the "blockade" and other Queens may not be placed in the grid:

How to solve the eight queens problem?

 

The so-called recursive backtracking, in essence, an enumeration method. This approach from the first row of the board began to try to put first the Queen, after successfully placed, recursive layer, and then follow the rules in the second row of the board to put the second queen. If the current position can not be placed, then move one space to the right to try again, if the place is successful, continue recursive layer, placed third Queen ......

 

If a layer looked through all the grid, it can not successfully placed, then back to the queen, the queen make a right on a grid, and then recursively. If the eight Queens are placed and meets the rules, then you get one correct solution.

 

说起来有些抽象,我们来看一看递归回溯的详细过程。

1.第一层递归,尝试在第一行摆放第一个皇后

2.第二层递归,尝试在第二行摆放第二个皇后(前两格被第一个皇后封锁,只能落在第三格):

3.第三层递归,尝试在第三行摆放第三个皇后(前四格被第一第二个皇后封锁,只能落在第五格):

4.第四层递归,尝试在第四行摆放第四个皇后(第一格被第二个皇后封锁,只能落在第二格):

 

5.第五层递归,尝试在第五行摆放第五个皇后(前三格被前面的皇后封锁,只能落在第四格):

6.由于所有格子都“绿了”,第六行已经没办法摆放皇后,于是进行回溯,重新摆放第五个皇后第八格。:

7.第六行仍然没有办法摆放皇后,第五行也已经尝试遍了,于是回溯到第四行,重新摆放第四个皇后第七格。:

8.继续摆放第五个皇后,以此类推......

八皇后问题的代码实现?

解决八皇后问题,可以分为两个层面:

1.找出第一种正确摆放方式,也就是深度优先遍历。

2.找出全部的正确摆放方式,也就是广度优先遍历。

在研究代码实现的时候,我们需要解决几个问题:

1.国际象棋的棋盘如何表示?

很简单,用一个长度是8的二维数组来表示即可。

static final int MAX = 8;
int qipan[][] = new int[MAX][MAX];

由于这里使用的是int数组,int的初始值是0,代表没有落子。当有皇后放置的时候,对应的元素值改为1。

在这里,二维数组的第一个维度代表横坐标,第二个维度代表纵坐标,并且从0开始。比如qipan[3][4]代表的是棋盘第四行第五列格子的状态。

2.如何判断皇后的落点是否合规?

定义一个check方法,传入新皇后的落点,通过纵向和斜向是否存在其他皇后来判断是否合规。

// 检查棋盘
	boolean cherk(int x, int y) {
		// 检查
		for (int i = 0; i < y; i++) {
			// 检查纵向
			if (qipan[x][i] != 0)
				return false;
			// 检查左斜
			if (x - 1 - i >= 0 && qipan[x - 1 - i][y - 1 - i] != 0)
				return false;
			// 检查右斜
			if (x + 1 + i < MAX && qipan[x + 1 + i][y - 1 - i] != 0)
				return false;
		}
		// 若可以放置则放置
		return true;
	}

3.如何进行递归回溯?

递归回溯是本算法的核心,代码逻辑有些复杂

PS:此处代码修改了,小灰的代码知识输出一个,本次修改完后输出92种

// 递归函数放置皇后
	boolean setqueen(int y) {
		// 行数超过8,则说明找出正确答案
		if (y == MAX)
			return true;
		// 遍历当前行,逐一验证
		for (int i = 0; i < MAX; i++) {
			// 为当前行清零,防止出现脏数据
			for (int x = 0; x < MAX; x++) {
				qipan[x][y] = 0;
			}
			// 检查是否符合规则,符合则进一步递归
			if (cherk(i, y)) {
				qipan[i][y] = 1;
				// 递归如果返回true说明下层找到解决,无需继续循环
				if (setqueen(y + 1)) {
					System.out.println("第"+ k + "个排列");
					k++;
					printqipan();
					return false;
				}
			}
		}
		return false;
	}

4.如何输出结果?

这个问题很简单,直接遍历二维数组并输出就可以。

PS:此处代码修改了,小灰的代码是输出二维数组,这个输出一个棋盘

	void printqipan() {
		// 遍历数组输出
		for (int j = 0; j < MAX; j++) {
			for (int i = 0; i < MAX; i++) {
				if (qipan[i][j]!=0) {
					System.out.print("●");
				}else {
					System.out.print("□");
				}
			}	
			System.out.println();
		}
	}

5.如何把这些方法串起来?

在main函数里分三步来调用:

第一步:初始化

第二步:递归摆放皇后

第三步:最后输出结果。

 

其中Queen是整个类的名字。


public class Main {

	
	public static void main(String[] args) {
		Queen queen = new Queen();
		queen.setqueen(0);
	}
	
}

最终输出如下:(图太长,就不粘贴了)

其实写到这,↑面的90%都是抄的,怎么算原创呢?

下面才是自己改的并写的。

java本来就是在小灰的基础上改的

附上java代码

public class Queen{

	static final int MAX = 8;
	int qipan[][] = new int[MAX][MAX];
	int k=1;
	
	
	// 检查棋盘
	boolean cherk(int x, int y) {
		// 检查
		for (int i = 0; i < y; i++) {
			// 检查纵向
			if (qipan[x][i] != 0)
				return false;
			// 检查左斜
			if (x - 1 - i >= 0 && qipan[x - 1 - i][y - 1 - i] != 0)
				return false;
			// 检查右斜
			if (x + 1 + i < MAX && qipan[x + 1 + i][y - 1 - i] != 0)
				return false;
		}
		// 若可以放置则放置
		return true;
	}

	// 递归函数放置皇后
	boolean setqueen(int y) {
		// 行数超过8,则说明找出正确答案
		if (y == MAX)
			return true;
		// 遍历当前行,逐一验证
		for (int i = 0; i < MAX; i++) {
			// 为当前行清零,防止出现脏数据
			for (int x = 0; x < MAX; x++) {
				qipan[x][y] = 0;
			}
			// 检查是否符合规则,符合则进一步递归
			if (cherk(i, y)) {
				qipan[i][y] = 1;
				// 递归如果返回true说明下层找到解决,无需继续循环
				if (setqueen(y + 1)) {
					System.out.println("第"+ k + "个排列");
					k++;
					printqipan();
					return false;
				}
			}
		}
		return false;
	}

	void printqipan() {
		// 遍历数组输出
		for (int j = 0; j < MAX; j++) {
			for (int i = 0; i < MAX; i++) {
				if (qipan[i][j]!=0) {
					System.out.print("●");
				}else {
					System.out.print("□");
				}
			}	
			System.out.println();
		}
	}

}

Main代码


public class Main {

	
	public static void main(String[] args) {
		Queen queen = new Queen();
		queen.setqueen(0);
	}
	
}

接下来C代码(.cpp文件下运行)

#include<stdio.h>

#define MAX 8
int qipan[MAX][MAX];
int k=1;

bool cherk(int x,int y);
bool setqueen(int y);
void printqipan();

int main(){
	printf("hello\n");
	setqueen(0);
	return 0;
}

	// 检查棋盘
	bool cherk(int x, int y) {
		// 检查
		for (int i = 0; i < y; i++) {
			// 检查纵向
			if (qipan[x][i] == 1)
				return false;
			// 检查左斜
			if (x - 1 - i >= 0 && qipan[x - 1 - i][y - 1 - i] == 1)
				return false;
			// 检查右斜
			if (x + 1 + i < MAX && qipan[x + 1 + i][y - 1 - i] == 1)
				return false;
		}
		// 若可以放置则放置
		return true;
	}

	// 递归函数放置皇后
	bool setqueen(int y) {
		// 行数超过8,则说明找出正确答案
		if (y == MAX)
			return true;
		// 遍历当前行,逐一验证
		for (int i = 0; i < MAX; i++) {
			// 为当前行清零,防止出现脏数据
			for (int x = 0; x < MAX; x++) {
				qipan[x][y] = 0;
			}
			// 检查是否符合规则,符合则进一步递归
			if (cherk(i, y)) {
				qipan[i][y] = 1;
				// 递归如果返回true说明下层找到解决,无需继续循环
				if (setqueen(y + 1)) {
					printf("第%d个排列\n",k);
					k++;
					printqipan();
					return false;
				}
			}
		}
		return false;
	}

	void printqipan() {
		// 遍历数组输出
		for (int i = 0; i < MAX; i++) {
			for (int j = 0; j < MAX; j++) {
			printf("%d",qipan[i][j]);
			}	
			printf("\n");
		}
	}

最后,是数据结构中的栈和此代码的结合,并规范使其在.c下运行

/**
*计算机科学与工程学院
*软件工程 数据结构实训第5组
*		八皇后
*要求:用到栈
*组长:莫言情难忘
*/
#include <stdio.h>
#include <stdlib.h>
#define STACK_INIT_SIZE 8

typedef int SElemType; 
#define MAX 8

int qipan[MAX][MAX];
int k=1;

bool cherk(int x,int y);//检查棋盘
bool setqueen(int y);//回溯

typedef struct{
	SElemType   *base;
	SElemType   *top;
	int stacksize;
}SqStack;

SqStack S;

//初始化
SqStack InitStack(SqStack *S)
{
	S->base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));
	if(!S->base)
		printf("申请失败");
	else
	{
		S->top=S->base;
		S->stacksize=STACK_INIT_SIZE;
	}
	return *S;
}

//进栈
push (SqStack *S,SElemType e)
{
	if(S->top -S->base >=STACK_INIT_SIZE )
		printf("栈满");
	else
	{
		*S->top=e;
		S->top++;
	}
}

//出栈
pop(SqStack *S)
{
	int e;
	if(S->top == S->base )
		printf("栈空");
	else
	{
		--S->top ;
		e=*S->top ;
		//printf("出栈元素:%d  \n",e);
	}
}

//输出
void Outputstack(SqStack *S)
{
	int shuzu[8];
	int i=0;
	SElemType *p;
	//数组使其逆序
	for(p=S->top-1;p>=S->base ;p--){
		shuzu[i]=*p;
		i++;
	}
	//输出
	for(i=MAX-1;i>=0;i--)
	{
		switch(shuzu[i]){
		case 1:printf("●□□□□□□□\n");break;
		case 2:printf("□●□□□□□□\n");break;
		case 3:printf("□□●□□□□□\n");break;
		case 4:printf("□□□●□□□□\n");break;
		case 5:printf("□□□□●□□□\n");break;
		case 6:printf("□□□□□●□□\n");break;
		case 7:printf("□□□□□□●□\n");break;
		case 8:printf("□□□□□□□●\n");break;
		}
	//	printf("%d    ",shuzu[i]);
	}
		
	printf("\n");
}


void main()
{
	//初始化栈
	S=InitStack(&S);
	setqueen(0);
}

// 检查棋盘
	bool cherk(int x, int y) {
		// 检查
		for (int i = 0; i < y; i++) {
			// 检查纵向
			if (qipan[x][i] == 1)
				return false;
			// 检查左斜
			if (x - 1 - i >= 0 && qipan[x - 1 - i][y - 1 - i] == 1)
				return false;
			// 检查右斜
			if (x + 1 + i < MAX && qipan[x + 1 + i][y - 1 - i] == 1)
				return false;
		}
		// 若可以放置则放置
		return true;
	}

	// 递归函数放置皇后
	bool setqueen(int y) {
		// 行数超过8,则说明找出正确答案
		if (y == MAX)
			return true;
		// 遍历当前行,逐一验证
		for (int i = 0; i < MAX; i++) {
			// 为当前行清零,防止出现脏数据
			for (int x = 0; x < MAX; x++) {
				qipan[x][y] = 0;
			}
			// 检查是否符合规则,符合则进一步递归
			if (cherk(i, y)) {
				qipan[i][y] = 1;
				push(&S,i+1);
				// 递归如果返回true说明下层找到解决,无需继续循环
				if (setqueen(y + 1)) {
					printf("第%d个排列\n",k);
					k++;
					Outputstack(&S);
					pop(&S);//出栈,栈里8变7,Y为7
					pop(&S);//出栈,栈里7变6,Y为7
					return false;
				}	
			}	
		}
		pop(&S);
		return false;
	}

结语:此篇文章是我们数据结构课程实训结束后发的,我以前就用java写过八皇后问题的源码,这次实训,借用了小灰的思维,加以更改,顺利的完成了此次实训。

 

 

Guess you like

Origin blog.csdn.net/qq_40176716/article/details/85336869