(Java实现) 广度优先搜索、深度优先搜索、基本回溯

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();
        }
    }

 

 

 

 

 

 

 

 

你可能感兴趣的:(算法)