1. 算法介绍
/*
广度优先搜索:
定义队列LinkedList (LinkedList队尾添加、队头删除)(Node自定义类:存当前数据和上一个下标)
定义qh、qe分别表示队头、队尾。以此表示逻辑上的出队入队
访问过的点进行标记
首先访问起始点、入队
队列不为空则{
记录队头下标
队头出队
遍历队头的邻接点{
未访问过则{
访问、入队
如果是最终结点则{
输出
结束
}
}
}
}
*/
city();//A城市到H城市,要经过的城市最少
maze();//走迷宫
/*
深度优先搜索:
首先走起始点,标记走过、可行
递归结点{
遍历结点的邻接点{
未走过、可行则{
走该节点,标记走过、可行
如果该节点是终点{
输出
结束
}不是终点则{
递归该结点
}
}
}
标记走过、不可行:因为结点相邻结点都遍历过了,皆不可到达终点
}
*/
mazeDepth();//走迷宫
tangram();//七巧板涂色
/*
基本回溯:递归方法实现(深度优先搜索思想)--输出所有方案
int[] arr=new int[n]
i=0~n-1,对于每个i实现方法
递归方法{
遍历所有条件{
满足条件{
赋值、标记
是最后一个i{
输出
}不是最后一个i{
递归i+1
}
清空标记: 输出/递归到无解,清空上一次标记,继续下一次尝试
}
}
}
*/
queen();//八皇后
horse();//马的遍历
2. 广度优先搜索:城市最少、走迷宫
/*
广度优先搜索--求城市间最短路径
*/
//A城市到H城市
public void city() {
//结点个数
int n = 8;
//图的邻接矩阵(0不可行,1可行)(自身不可访问)
int[][] arr = {{0, 1, 1, 1, 0, 1, 0, 0}, {1, 0, 0, 0, 0, 1, 0, 0}, {1, 0, 0, 1, 1, 0, 0, 0}, {1, 0, 1, 0, 0, 0, 1, 0},
{0, 0, 1, 0, 0, 0, 1, 1}, {1, 1, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 1, 1, 0, 0, 1}, {0, 0, 0, 0, 1, 1, 1, 0}};
//结点是否访问过(0未访问过,1访问过)
int[] visit = {0, 0, 0, 0, 0, 0, 0, 0};
//队列
LinkedList list = new LinkedList();
int qh = 0;//队头下标(出队,队头+1)
int qe = 0;//队尾下标(入队,队尾+1)
//访问起始点
visit[0] = 1;
//入队
list.add(new Node(-1, 0));
qe++;
while (qe > qh) {
//出队
int index = qh;//出队元素的下标
qh++;
int data = list.get(index).getData();//出队元素
//访问 出队结点 的相邻结点
for (int j = 0; j < n; j++) {
if (arr[data][j] == 1 && visit[j] != 1) {//可行,未访问过
//访问
visit[j] = 1;
//入队
Node node = new Node(index, j);
list.add(node);
qe++;
if (j == n - 1) {//最后一个
cityOut(list);
}
}
}
}
}
//城市输出 正向输出
private void cityOut(LinkedList list) {
//结点名称
String[] name = {"A", "B", "C", "D", "E", "F", "G", "H"};
//输出路径
LinkedList listPath = new LinkedList();
int n = list.size();
listPath.addFirst(name[list.get(n - 1).getData()]);
int pre = list.get(n - 1).getPre();//上一个的下标
while (pre != -1) {
listPath.addFirst(name[list.get(pre).getData()]);
pre = list.get(pre).getPre();
}
System.out.println("城市间最短路径 正向输出:");
System.out.println(listPath);
}
/*
广度优先搜索--走迷宫
*/
//找一条可行路径(0,0)到(7,7)
public void maze() {
//迷宫的行、列
int n = 8, m = 8;
//迷宫(0可行,1不可行)(-1走过可行,2走过不可行)
int[][] arr = {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 1, 0},
{0, 1, 0, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 1, 1}, {0, 1, 0, 0, 1, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 0}};
//四个方向
int[] fx = {1, -1, 0, 0}, fy = {0, 0, 1, -1};
//队列
LinkedList list = new LinkedList();
int qh = 0, qe = 0;//队头、队尾
//从 [0,0] 开始
//访问
arr[0][0] = -1;
//入队
list.add(new Node(-1, 0, 0));
qe++;
while (qh < qe) {
//(逻辑)出队
int index = qh;//出队元素下标
qh++;
Node remove = list.get(index);
//遍历 出队结点 的相邻结点(上下左右)
for (int k = 0; k < 4; k++) {
int i = remove.getX() + fx[k];
int j = remove.getY() + fy[k];
//出了迷宫
if (i < 0 || i > 7 || j < 0 || j > 7) {
continue;
}
if (arr[i][j] == 0) {//可行,未访问过
//访问
arr[i][j] = -1;
//入队
Node node = new Node(index, i, j);
list.add(node);
qe++;
if (i == n - 1 && j == m - 1) {
mazeOut(list);
}
}
}
}
}
//迷宫输出 倒向输出
private void mazeOut(LinkedList list) {
int n = list.size();
System.out.println("迷宫 倒向输出:");
System.out.print(list.get(n - 1).toStringXY() + "\t");
int pre = list.get(n - 1).getPre();//上一个的下标
while (pre != -1) {
System.out.print(list.get(pre).toStringXY() + "\t");
pre = list.get(pre).getPre();
}
System.out.println();
}
//广度优先搜索使用--队列存的内容
class Node {
private Integer pre;//前一个结点在队列的下标
private Integer data;//当前数据
//二维数组表示当前结点 array[x][y]
private Integer x;
private Integer y;
public String toStringXY() {
return new StringBuilder().append("(").append(this.x)
.append(",").append(this.y).append(")").toString();
}
public Node(Integer pre, Integer x, Integer y) {
this.pre = pre;
this.x = x;
this.y = y;
}
public Integer getX() {
return x;
}
public void setX(Integer x) {
this.x = x;
}
public Integer getY() {
return y;
}
public void setY(Integer y) {
this.y = y;
}
public Node(Integer pre, Integer data) {
this.data = data;
this.pre = pre;
}
public Integer getData() {
return data;
}
public void setData(Integer data) {
this.data = data;
}
public Integer getPre() {
return pre;
}
public void setPre(Integer pre) {
this.pre = pre;
}
}
3. 深度优先搜索:走迷宫、七巧板涂色
/*
深度优先搜索--走迷宫
*/
//迷宫参数
int nDepth = 8, mDepth = 8;//迷宫的行、列
int[][] arrDepth = {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 1, 0},
{0, 1, 0, 0, 0, 0, 1, 0}, {0, 1, 0, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 1, 1},
{0, 1, 0, 0, 1, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 0}};//迷宫(0可行,1不可行)(-1走过可行,2走过不可行)
int[] fxDepth = {1, 0, 0, -1}, fyDepth = {0, -1, 1, 0};//四个方向
public void mazeDepth() {
//标记起始点--走过可行
arrDepth[0][0] = -1;
//递归结点
depth(0, 0);
}
//结点递归
private void depth(int x, int y) {
//遍历结点的邻结点
for (int i = 0; i < 4; i++) {
int newX = x + fxDepth[i];
int newY = y + fyDepth[i];
//没出迷宫、未走过、可行
if (newX >= 0 && newX < nDepth && newY >= 0 && newY < mDepth
&& arrDepth[newX][newY] == 0) {
//标记走过、可行
arrDepth[newX][newY] = -1;
//终点则输出,不是终点则递归该结点
if (newX == nDepth - 1 && newY == mDepth - 1) {
mazeDepthOut();
return;
} else {
depth(newX, newY);
}
}
}
//邻接点都不能走到终点,标记:走过不可行
arrDepth[x][y] = 2;
}
//迷宫图输出
private void mazeDepthOut() {
System.out.println("迷宫行走图:");
for (int i = 0; i < nDepth; i++) {
for (int j = 0; j < mDepth; j++) {
if (arrDepth[i][j] == -1) {
System.out.print("V" + "\t");
} else {
System.out.print("*" + "\t");
}
}
System.out.println();
}
//输出迷宫路径
mazeDepthPathOut();
System.out.println();
}
//输出迷宫路径
private void mazeDepthPathOut() {
System.out.println("迷宫行走路径:");
System.out.print("(0,0)" + "\t");
arrDepth[0][0] = -2;//标记输出过
mazeDepthPathOut(0, 0);
}
//输出迷宫路径
private void mazeDepthPathOut(int x, int y) {
for (int i = 0; i < 4; i++) {
int newX = x + fxDepth[i];
int newY = y + fyDepth[i];
if (newX >= 0 && newX < nDepth && newY >= 0 && newY < mDepth
&& arrDepth[newX][newY] == -1) {
System.out.print("(" + newX + "," + newY + ")" + "\t");
arrDepth[newX][newY] = -2;//标记输出过
mazeDepthPathOut(newX, newY);
}
}
}
/*
深度优先搜索--七巧板涂色
*/
//参数
//0相邻,1不相邻(自身设置1)
int[][] arrTan = {{1, 0, 1, 1, 0, 1, 0}, {0, 1, 1, 0, 1, 0, 1}, {1, 1, 1, 0, 1, 1, 0},
{1, 0, 0, 1, 1, 0, 0}, {0, 1, 1, 1, 1, 1, 0}, {1, 0, 1, 0, 1, 1, 1},
{0, 1, 0, 0, 0, 1, 1}};
//七巧板--0表示未涂色,涂色:1,2,3,4
int[] tangram = {0, 0, 0, 0, 0, 0, 0};
//计数
int num = 0;
public void tangram() {
//涂第一个
tangram[0] = 1;
num++;
//递归结点
tangram(0);
}
//递归结点
private void tangram(int i) {
for (int j = 0; j < arrTan[i].length; j++) {
//相邻、未涂色
if (arrTan[i][j] == 0 && tangram[j] == 0) {
//给j涂色:看j的相邻结点都有哪些颜色
int colour = 0;
LinkedList index = new LinkedList();//颜色
for (int z = 0; z < arrTan[j].length; z++) {
if (arrTan[j][z] == 0 && tangram[z] != 0) {
index.add(tangram[z]);
}
}
for (int c = 1; c <= 4; c++) {
if (!index.contains(c)) {
colour = c;
break;
}
}
tangram[j] = colour;
num++;
//终点--输出;非终点--递归该结点
if (num == arrTan.length) {
tangramOut();
} else {
tangram(j);
}
/*
//其它方案
tangram[j] = 0;
num--;
*/
}
}
}
private void tangramOut() {
System.out.println("七巧板涂色:");
for (int i = 0; i < tangram.length; i++) {
System.out.print(tangram[i] + "\t");
}
System.out.println();
}
4. 基本回溯:八皇后、马的遍历
/*
回溯法--八皇后 (深度优先搜索思想)
*/
//八皇后参数
int nQueen = 8;//8*8方格(0~7)
int[] arrQueen = new int[nQueen];//八皇后:arr[i]处的值j表示 第i行的皇后在j列
int[] colQueen = new int[nQueen];//n列
int[] diaOneQueen = new int[2 * nQueen];//2n-1条主对角线,x-y+n相等(1~2n-1)
int[] diaTwoQueen = new int[2 * nQueen];//2n-1条负对角线,x+y相等(0~2n-2)
int numQueen = 0;//有多少种可能
public void queen() {
System.out.println("八皇后位置:");
//从第一行开始
queen(0);
}
//入参:行号
private void queen(int i) {
//遍历所有列
for (int j = 0; j < nQueen; j++) {
if (colQueen[j] == 0 && diaOneQueen[i - j + nQueen] == 0 && diaTwoQueen[i + j] == 0) {
//放置
arrQueen[i] = j;
//设置有值
colQueen[j] = 1;
diaOneQueen[i - j + nQueen] = 1;
diaTwoQueen[i + j] = 1;
//是最后一个输出, 不是则继续下一个
if (i == nQueen - 1) {
queenOut();
} else {
queen(i + 1);
}
//有输出/递归方法queen结束:运行上一个queen的以下部分(清空上次赋值,继续下一列尝试)
colQueen[j] = 0;
diaOneQueen[i - j + nQueen] = 0;
diaTwoQueen[i + j] = 0;
}
}
}
//八皇后输出
private void queenOut() {
System.out.print(++numQueen + " 八皇后位置: " + "\t");
for (int i = 0; i < nQueen; i++) {
System.out.print("(" + i + "," + arrQueen[i] + ")" + "\t");
}
System.out.println();
}
/*
基本回溯--马的遍历
*/
//马的遍历参数
int nHorse = 4, mHorse = 5;//棋盘
int[][] arrHorse = new int[nHorse][mHorse];
int[] fx = {2, 2, -2, -2, 1, 1, -1, -1};//八个方向
int[] fy = {1, -1, 1, -1, 2, -2, 2, -2};
int stepHorse = 0;//第几步
int numHorse = 0;
public void horse() {
//起始点
arrHorse[0][0] = ++stepHorse;
horse(0, 0);
}
private void horse(int x, int y) {
for (int z = 0; z < 8; z++) {
int i = x + fx[z];
int j = y + fy[z];
if (i >= 0 && i < nHorse && j >= 0 && j < mHorse && arrHorse[i][j] == 0) {
arrHorse[i][j] = ++stepHorse;//走
if (stepHorse >= nHorse * mHorse) {
horseOut();
} else {
horse(i, j);
}
arrHorse[i][j] = 0;
stepHorse--;
}
}
}
private void horseOut() {
System.out.println("马的遍历图" + (++numHorse) + ":");
for (int i = 0; i < nHorse; i++) {
for (int j = 0; j < mHorse; j++) {
System.out.print(arrHorse[i][j] + "\t");
}
System.out.println();
}
}