目录
简要解析
?
回溯法
一解(预见算法、栈实现回溯)
多解(预见算法、栈实现回溯)
全解(回溯,暴力递归)
运行结果图
其他代码
变量
构造函数
判断该点的direction方向是否可走
预见算法
打印路径
打印棋盘
多解的全部代码
end
简要解析
?
?
?
?
?
回溯法
回溯法都叫backTrack
一解(预见算法、栈实现回溯)
/**
? ? * x[] 走过的方向
? ? * dirList 第i层的可选方向
? ? * p 点坐标
? ? * i 层数
? ? * chessboard[][] 记录走法
? ? * 需要入栈的变量 : dirList p
? ? * 技术难题: dirList的入栈出栈 与使用
? ? * 时间复杂度O(n^2*n)=O(n^3) <--> 空间复杂度O(n*n)
? ? */
public void backTrack(Point p) //非递归方式的 栈 深度优先 回溯
? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //栈如何转化多个分叉的递归
? ? ? ?int[]x=new int[n*n+1];
? ? ? ?pathlen=1;
? ? ? ?int i=1;
? ? ? ?chessboard[p.x][p.y]=1;
? ? ? ?LinkedStack<Point> stack=new LinkedStack<>();
? ? ? ?stack.push(p);
? ? ? ?SeqList<Integer> dirList;
? ? ? ?while (p!=null || !stack.isEmpty()) ?//当前p在i+1层
? ? ? {
? ? ? ? ? ?if(i==n*n)
? ? ? ? ? {
? ? ? ? ? ? ? ?count++;
? ? ? ? ? ? ? ?System.out.println("第"+count+"个解");
? ? ? ? ? ? ? ?stack.pop();
? ? ? ? ? ? ? ?printMat();
? ? ? ? ? ? ? ?chessboard[p.x][p.y]=0;
? ? ? ? ? ? ? ?i--;
? ? ? ? ? ? ? ?break;
? ? ? ? ? }
? ? ? ? ? ?dirList=select(p);
? ? ? ? ? ? ? ?//对于预见算法select 当到达第24层时,p的下一节点只有一个,而且宽度为0
? ? ? ? ? ?if(x[i]<dirList.size() ) //找到可行解
? ? ? ? ? {
? ? ? ? ? ? ? ?p=restrict(p,dirList.get(x[i]));
? ? ? ? ? ? ? ?x[i]++;
? ? ? ? ? ? ? ?i++;
? ? ? ? ? ? ? ?pathlen=i;
? ? ? ? ? ? ? ?chessboard[p.x][p.y]=i;
? ? ? ? ? ? ? ?stack.push(p);
? ? ? ? ? }
? ? ? ? ? ?else ?//没有下一个可行点,回溯
? ? ? ? ? {
? ? ? ? ? ? ? ?chessboard[stack.peek().x][stack.peek().y] = 0;
? ? ? ? ? ? ? ?stack.pop();
? ? ? ? ? ? ? ?p=stack.peek();
? ? ? ? ? ? ? ?x[i]=0;
? ? ? ? ? ? ? ?i--;
? ? ? ? ? }
? ? ? }
? }
多解(预见算法、栈实现回溯)
一个解就是找到一个解就退出循环,结束程序
/**
? ? * x[] 走过的方向
? ? * dirList 第i层的可选方向
? ? * p 点坐标
? ? * i 层数
? ? * chessboard[][] 记录走法
? ? * 需要入栈的变量 : dirList p
? ? * 技术难题: dirList的入栈出栈 与使用
? ? * 时间复杂度O(n^2*n)=O(n^3) <--> 空间复杂度O(n*n)
? ? */
public void backTrack(Point p) //非递归方式的 栈 深度优先 回溯
? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //栈如何转化多个分叉的递归
? ? ? ?int[]x=new int[n*n+1];
? ? ? ?pathlen=1;
? ? ? ?int i=1;
? ? ? ?chessboard[p.x][p.y]=1;
? ? ? ?LinkedStack<Point> stack=new LinkedStack<>();
? ? ? ?stack.push(p);
? ? ? ?SeqList<Integer> dirList;
? ? ? ?while (p!=null || !stack.isEmpty()) ?//当前p在i+1层
? ? ? {
? ? ? ? ? ?if(i==n*n)
? ? ? ? ? {
? ? ? ? ? ? ? ?count++;
? ? ? ? ? ? ? ?System.out.println("第"+count+"个解");
? ? ? ? ? ? ? ?stack.pop();
? ? ? ? ? ? ? ?printMat();
? ? ? ? ? ? ? ?chessboard[p.x][p.y]=0;
? ? ? ? ? ? ? ?i--;
// ? ? ? ? ? ? ? break;
? ? ? ? ? }
? ? ? ? ? ?dirList=select(p);
? ? ? ? ? ? ? ?//对于预见算法select 当到达第24层时,p的下一节点只有一个,而且宽度为0
? ? ? ? ? ?if(x[i]<dirList.size() ) //找到可行解
? ? ? ? ? {
? ? ? ? ? ? ? ?p=restrict(p,dirList.get(x[i]));
? ? ? ? ? ? ? ?x[i]++;
? ? ? ? ? ? ? ?i++;
? ? ? ? ? ? ? ?pathlen=i;
? ? ? ? ? ? ? ?chessboard[p.x][p.y]=i;
? ? ? ? ? ? ? ?stack.push(p);
? ? ? ? ? }
? ? ? ? ? ?else ?//没有下一个可行点,回溯
? ? ? ? ? {
? ? ? ? ? ? ? ?chessboard[stack.peek().x][stack.peek().y] = 0;
? ? ? ? ? ? ? ?stack.pop();
? ? ? ? ? ? ? ?p=stack.peek();
? ? ? ? ? ? ? ?x[i]=0;
? ? ? ? ? ? ? ?i--;
? ? ? ? ? }
? ? ? }
? }
全解(回溯,暴力递归)
protected void backTrack(int i,Point p)
{
? ?if(i<=this.n*this.n)
? {
? ? ? ?this.pathlen=i;
? ? ? ?chessboard[p.x][p.y]=i;
? ? ? ?if(i==this.n*this.n)
? ? ? ? ? ?return;
?
? ? ? ?for(int j=1;j<=8;j++)
? ? ? {
? ? ? ? ? ?Point next=restrict(p,j); //八个方向 每个方向都递归
? ? ? ? ? ?if(next!=null) ?//有下一个位置,则前进到下一个位置
? ? ? ? ? {
? ? ? ? ? ? ? ?backTrack(i+1,next);
?
? ? ? ? ? ? ? ?if(i+1==pathlen) ?//下一步走完 就没有后路了,所以路径终止在p0到p(i+1)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ?if(show)
? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ?path++;
? ? ? ? ? ? ? ? ? ? ? ?System.out.print("第"+path+"条路径");
?
? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ?if(i+1==this.n*this.n)
? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ?count++;
? ? ? ? ? ? ? ? ? ? ? ?System.out.print("第"+count+"个解");
? ? ? ? ? ? ? ? ? ? ? ?System.out.print("时刻:"+(System.currentTimeMillis()-startTime)/1000+"s");
? ? ? ? ? ? ? ? ? ? ? ?System.out.println();
? ? ? ? ? ? ? ? ? ? ? ?list.add(new ChessBoard(chessboard));
? ? ? ? ? ? ? ? ? ? ? ?// ? ? ? ? ? ? ? ? ? ? ? ? ? this.print(); //输出一条路径/一个解
? ? ? ? ? ? ? ? ? ? ? ?printMat();
? ? ? ? ? ? ? ? ? }
?
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?chessboard[next.x][next.y]=0; ?//next走完后,退回到前一个递归函数的状态,走下一个next方向
? ? ? ? ? }
? ? ? }
? }
}
运行结果图
?
其他代码
变量
protected final int n; ?//棋盘规格为 n*n
protected int chessboard[][];
protected boolean show;
protected int pathlen,path,count;
构造函数
public Knight_Stack(int n, int x, int y, boolean show)
{
? ?if(n>=3 && n<=50)
? ? ? ?this.n = n;
? ?else
? ? ? ?throw new IllegalArgumentException("n="+n);
? ?chessboard=new int[n][n]; //初始值为0
? ?System.out.println("n="+n+",point="+new Point(x,y));
? ?this.show=show;
? ?pathlen=0;
? ?path=0;
? ?count=0;
// backTrack(1,new Point(x,y)) ; //递归
? ?backTrack(new Point(x,y)); ?//栈
? ?System.out.println("共有"+count+"个解");
}
判断该点的direction方向是否可走
? ?protected Point restrict(Point point,int direction)
? {
? ? ? ?int x=point.x,y=point.y;
? ? ? ?switch(direction)
? ? ? {
? ? ? ? ? ?case 1:x-=2 ;y++; break;
? ? ? ? ? ?case 2:x--; ?y+=2;break;
? ? ? ? ? ?case 3:x++; ?y+=2;break;
? ? ? ? ? ?case 4:x+=2; y+=1;break;
? ? ? ? ? ?case 5:x+=2; y--; break;
? ? ? ? ? ?case 6:x+=1; y-=2;break;
? ? ? ? ? ?case 7:x-=1; y-=2;break;
? ? ? ? ? ?case 8:x-=2; y-=1;break;
? ? ? }
? ? ? ?if(x>=0 && x<this.n && y>=0 && y<this.n && this.chessboard[x][y]==0)
? ? ? ? ? ?return new Point(x,y); ? ? ?//0表示还没有经过,没有走过
? ? ? ?return null;
? }
预见算法
protected SeqList<Integer> select(Point p) ?//返回大小为n的线性表,方向按照 路的“宽度”从小到大存放
? { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //求得下一步8个方向位置的路的“宽度”,并将元素从大到小拍放在线性表,,不如排序线性表。。
? ? ? ?if(show)
? ? ? {
? ? ? ? ? ?System.out.println("当前点为:" + p);
// ? ? ? ? ? print();//打印当前路径
? ? ? ? ? ?printMat();
? ? ? }
// ? ? ? 计算宽度,以当前点为下一节点,计算自己可走的路数
? ? ? ?Point next;
? ? ? ?int minRoad=8;
? ? ? ?SeqList<Integer> list=new SeqList<>();
? ? ? ?for(int k=1;k<=8;k++) ? //选择宽度最小的方向,并且将宽度最小的方向
? ? ? {
? ? ? ? ? ?int avl = 0;
? ? ? ? ? ?next=restrict(p,k);
? ? ? ? ? ?if(next!=null && chessboard[next.x][next.y]==0) ?//下一步,不仅要有,还得时未走过的
? ? ? ? ? {
? ? ? ? ? ? ? ?for (int i = 1; i <= 8; i++)
? ? ? ? ? ? ? ? ? ?if (restrict(next, i) != null)
? ? ? ? ? ? ? ? ? ? ? ?avl++;
? ? ? ? ? ? ? ?if(avl<minRoad)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ?minRoad=avl;
? ? ? ? ? ? ? ? ? ?list.clear();
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?if(avl==minRoad)
? ? ? ? ? ? ? ? ? ?list.insert(k);
? ? ? ? ? }
? ? ? }
? ? ? ?return list;
? }
打印路径
? ?protected void print() ?//输出路径 从点(x,y)开始
? {
? ? ? ?String str="[";
? ? ? ?for(int k=1;k<=n*n;k++)
? ? ? ? ? ?for(int i=0;i<n;i++)
? ? ? ? ? ? ? ?for(int j=0;j<n;j++)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ?if(chessboard[i][j]==k) ?//得到顺序路径上的一点p(i,j)
? ? ? ? ? ? ? ? ? ? ? ?str+=new Point(i,j)+",";
? ? ? ? ? ? ? }
? ? ? ?if(str.length()>1)
? ? ? ? ? ?str=str.substring(0,str.length()-1);
? ? ? ?System.out.println(str+"]");
? }
打印棋盘
private void printMat()
? {
? ? ? ?for (int i=0;i<n;i++)
? ? ? {
? ? ? ? ? ?for (int j = 0; j < n; j++)
? ? ? ? ? ? ? ?System.out.printf("%5d",chessboard[i][j]);
? ? ? ? ? ?System.out.println();
? ? ? }
? }
多解的全部代码
package chapter_10_courseDesign;
import chapter_1_1_SeqLIst.SeqList;
import chapter_3_1_Stack_Queue.LinkedStack;
public class Knight //二维数组chessBoard存储n*n棋盘的一个解,使用预见算法 回溯算法
{
protected final int n; //棋盘规格为 n*n
protected int chessboard[][];
protected boolean show;
protected int pathlen,path,count;
public Knight(int n, int x, int y, boolean show)
{
if(n>=3 && n<=12)
this.n = n;
else
throw new IllegalArgumentException("n="+n);
chessboard=new int[n][n]; //初始值为0
System.out.println("n="+n+",point="+new Point(x,y));
this.show=show;
pathlen=1;
path=0;
count=0;
// backTrack(1,new Point(x,y));
backTrack(new Point(x,y));
System.out.println("共有"+count+"个解");
}
/**
* x[] 走过的方向
* dirList 第i层的可选方向
* p 点坐标
* i 层数
* chessboard[][] 记录走法
* 需要入栈的变量 : dirList p
* 技术难题: dirList的入栈出栈 与使用
* 时间复杂度O(n^2*n)=O(n^3) <--> 空间复杂度O(n*n)
*/
public void backTrack(Point p) //非递归方式的 栈 深度优先 回溯
{ //栈如何转化多个分叉的递归
int[]x=new int[n*n+1];
pathlen=1;
int i=1;
chessboard[p.x][p.y]=1;
LinkedStack<Point> stack=new LinkedStack<>();
stack.push(p);
SeqList<Integer> dirList;
while (p!=null || !stack.isEmpty()) //当前p在i+1层
{
if(i==n*n)
{
count++;
System.out.println("第"+count+"个解");
stack.pop();
printMat();
chessboard[p.x][p.y]=0;
i--;
// break;
}
dirList=select(p);
//对于预见算法select 当到达第24层时,p的下一节点只有一个,而且宽度为0
if(x[i]<dirList.size() ) //找到可行解
{
p=restrict(p,dirList.get(x[i]));
x[i]++;
i++;
pathlen=i;
chessboard[p.x][p.y]=i;
stack.push(p);
// dirStack.push(dirList);
}
else //没有下一个可行点,回溯
{
chessboard[stack.peek().x][stack.peek().y] = 0;
stack.pop();
// dirStack.pop();
p=stack.peek();
x[i]=0;
i--;
}
}
}
protected void backTrack(int i,Point p)
{
if(i<=this.n*this.n)
{
this.pathlen=i;
chessboard[p.x][p.y]=i;
if(i==this.n*this.n) //到达叶子节点,直接返回,回溯
return;
SeqList<Integer> direList=this.select(p); //预见算法给出的方向选择
for(int j=0;j<direList.size();j++)
{
Point next=restrict(p,direList.get(j));
if(next!=null) //有下一个位置,则前进到下一个位置
{
backTrack(i+1,next);
if(i+1==pathlen) //下一步走完 就没有后路了,所以路径终止在p0到p(i+1)
{
if(show)
{
path++;
System.out.print("第"+path+"条路径");
}
if(i+1==this.n*this.n)
{
count++;
System.out.print("第"+count+"个解");
}
System.out.println();
printMat();
}
chessboard[next.x][next.y]=0; //next点走完后,退回到前一个递归函数的状态,走下一个next方向
}
}
}
}
protected void print() //输出路径 从点(x,y)开始
{
String str="[";
for(int k=1;k<=n*n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(chessboard[i][j]==k) //得到顺序路径上的一点p(i,j)
str+=new Point(i,j)+",";
}
if(str.length()>1)
str=str.substring(0,str.length()-1);
System.out.println(str+"]");
}
private void printMat()
{
for (int i=0;i<n;i++)
{
for (int j = 0; j < n; j++)
System.out.printf("%3d",chessboard[i][j]);
System.out.println();
}
}
protected SeqList<Integer> select(Point p) //返回大小为n的线性表,方向按照 路的“宽度”从小到大存放
{ //求得下一步8个方向位置的路的“宽度”,并将元素从大到小拍放在线性表,,不如排序线性表。。
if(show)
{
System.out.println("当前点为:" + p);
print();//打印当前路径
// printMat();
}
// 计算宽度,以当前点为下一节点,计算自己可走的路数
Point next;
int minRoad=8;
SeqList<Integer> list=new SeqList<>();
for(int k=1;k<=8;k++) //选择宽度最小的方向,并且将宽度最小的方向
{
int avl = 0;
next=restrict(p,k);
if(next!=null && chessboard[next.x][next.y]==0) //下一步,不仅要有,还得时未走过的
{
for (int i = 1; i <= 8; i++)
if (restrict(next, i) != null)
avl++;
if(avl<minRoad)
{
minRoad=avl;
list.clear();
}
if(avl==minRoad)
list.insert(k);
}
}
return list;
}
protected Point restrict(Point point,int direction)
{
int x=point.x,y=point.y;
switch(direction)
{
case 1:x-=2 ;y++; break;
case 2:x--; y+=2;break;
case 3:x++; y+=2;break;
case 4:x+=2; y+=1;break;
case 5:x+=2; y--; break;
case 6:x+=1; y-=2;break;
case 7:x-=1; y-=2;break;
case 8:x-=2; y-=1;break;
}
if(x>=0 && x<this.n && y>=0 && y<this.n && this.chessboard[x][y]==0)
return new Point(x,y); //0表示还没有经过,没有走过
return null;
}
public static void main(String[] args)
{
long start=System.currentTimeMillis();
Knight knight=new Knight(5,0,0,true);
System.out.println("用时"+(System.currentTimeMillis()-start)+"ms");
}
}
end
|