骑士游历(Java课设)

目录

简要解析

回溯法

一解(预见算法、栈实现回溯)

多解(预见算法、栈实现回溯)

全解(回溯,暴力递归)

运行结果图

其他代码

变量

构造函数

判断该点的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

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值