Java算法

Java算法

这篇文章将会分为三个部分,分别是:

  • 深度优先算法:获得一条路径
  • 广度优先算法:获得最短路径的长度
  • 广度优先算法:在有传送门的迷宫中寻找最短路径

一、深度优先算法:获得一条路径

在这个题目中,不涉及传送门,地图可以这样表示:

其中,1 的位置表示了墙,即不可使用,0 的位置则为路,因为现在值要求获得一条路径,是不是最佳路径我们不管,所以我们可以使用“一条路走到黑”的思路,深度优先。

(注:这里假设的启点为左上,重点为右下,在启点与重点非这种情形下,算法仍然适用)。

具体的做法是这样的:
首先我们先定义一个Position类来存储一下当前位置,也可以用数组,这里新建类是方便表示和理解:

class Position{
        int row;
        int cow;
        public Position(int row,int cow){
            this.cow = cow;
            this.row = row;
        }
        public void show(){
            System.out.println(this.row + "    " + this.cow);
        }
    }

这里我还定义了一个show方法,是我在写的时候调试用的,大家可以不管或者直接删除。

然后我们需要设立一个栈,当然也可使用队列来存储这条路径,其次,我们还维护一个访问状态的二维数组,避免在一条圈上反复寻找,造成死循环。

class Position{
        int row;
        int cow;
        public Position(int row,int cow){
            this.cow = cow;
            this.row = row;
        }
        public void show(){
            System.out.println(this.row + "    " + this.cow);
        }
    }
这里我还定义了一个show方法,是我在写的时候调试用的,大家可以不管或者直接删除。

然后我们需要设立一个栈,当然也可使用队列来存储这条路径,其次,我们还维护一个访问状态的二维数组,避免在一条圈上反复寻找,造成死循环。

public List<int[]> solutionDFS(int[][] nums){
        int rows = nums.length;
        int cows = nums[0].length;
        int[][] visited = new int[rows][cows];
        Stack<Position> stack = new Stack<>();
        Position p = new Position(0,0);    // 记录一下当前位置,在启点
        stack.add(p);
        visited[0][0] = 1;     //  访问状态设置为 1 ,代表已经访问过了
        Position temp;
        //  只要找到了终点就退出循环
        //  始终没有找到,也会导致栈弹空
        while (!stack.isEmpty()&& !(p.row == rows-1 && p.cow == cows-1)){
            p = stack.peek();     // 获取上一个访问过的位置
           //  按照方向  → ↓  ←  ↑的顺序依次进行试探性的走一步
           //  如果能走通(在迷宫范围内,不是墙,而且没有访问过,就可以认为是可以走)
            if (p.cow+1<cows && nums[p.row][p.cow+1] == 0 && visited[p.row][p.cow+1] != 1){
                temp = new Position(p.row,p.cow+1);
                stack.add(temp);
                visited[temp.row][temp.cow] = 1;
            }else if (p.row+1<rows && nums[p.row+1][p.cow] == 0 && visited[p.row+1][p.cow] != 1){
                temp = new Position(p.row+1,p.cow);
                stack.add(temp);
                visited[temp.row][temp.cow] = 1;
            }else if (p.cow-1>-1 && nums[p.row][p.cow-1] == 0 && visited[p.row][p.cow-1] != 1) {
                temp = new Position(p.row,p.cow-1);
                stack.add(temp);
                visited[temp.row][temp.cow] = 1;
            }else if (p.row-1 >-1 && nums[p.row-1][p.cow] == 0 && visited[p.row-1][p.cow] != 1){
                temp = new Position(p.row-1,p.cow);
                stack.add(temp);
                visited[temp.row][temp.cow] = 1;
            }else {
            // 如果没有尝试了四个方向都没有走通,说明上一个点的选取有问题,直接弹出
                stack.pop();
            }
        }
       // 最后根据还在栈里的的元素,推导出一挑可用路径
        if (stack.isEmpty()) return new LinkedList<>();
        Deque<int[]> deque = new LinkedList<>();
        for (Position po:stack) {
            deque.addLast(new int[]{po.row,po.cow});
        }
        return (List)deque;
    }

这是针对上面的迷宫的一个输出:

二、广度优先算法,获得最短路径

如果想要获得一条最短路径,那么我们可以使用广度优先的思路,“一层一层的剥开我的心”

!啊,回来!

广度优先的思路其实也很容易理解,拿到一个点后,根据这个点的步数,更新这个点周围四个方向上的最小步数,直到全局稳定(也就是没有更小值可以更新了)

public int solutionBFS(int[][] nums){
        int rows = nums.length;
        int cows = nums[0].length;
        int[][] count = new int[rows][cows];
    //  首先对计数的数组进行初始化
        for (int i = 0;i<rows;i++){
            Arrays.fill(count[i],Integer.MAX_VALUE);
        }
        count[0][0] = 0;
   //  由于深度优先算法是和遍历的层数有关的,所以我们使用双向链表来操作
   //  前面添加,后面取用(还可以使用两个栈来进行交替使用)
        Deque<Position> deque = new LinkedList<>();
        Position p = new Position(0,0);
        deque.add(p);
        int[] r = {0,1,0,-1};
        int[] c = {1,0,-1,0};
        while (!deque.isEmpty()){
            p = deque.pollLast();
            for (int i = 0; i<4;i++){
                int tempR = p.row+r[i];
                int tempC = p.cow+c[i];
                if (tempR>-1 && tempR<rows && tempC>-1 && tempC<cows && nums[tempR][tempC] == 0){
                    //  如果能够进行更新,那就将这个位置再次压入队列中,等待下一次更新
                    if (count[tempR][tempC] > count[p.row][p.cow]+1){
                        count[tempR][tempC] = count[p.row][p.cow]+1;
                        Position temp = new Position(tempR,tempC);
                        deque.addFirst(temp);
                    }
                }
            }
        }
        return count[rows-1][cows-1];
    }

这是示例迷宫的输出:

三、广度优先算法:在有传送门的迷宫中寻找最短路径

这是我们这次主要要说的迷宫,有传送门的迷宫。
一个示例的带有传送门的地图可能是这样的:

其中:

-2 表示启点,-3表示终点,0表示普通路径,-1表示墙,大于0的数字则表示传送门(能够保证传送门成对出现)。
思考过程是这样的:
由于传送门之间的穿送是不记录步数的,直觉的思路是:当遇到一个传送门时,直接传送,进而继续进行广度优先搜索(寻找最短路径)。

我认为这个思路可行,但是实现起来可能比较麻烦,因为:

  • 一方面,你不知道传送门用的顺序
  • 另一方面,你不知道传送门使用的次数,可能是一次,也可能是0次。而广度优先,对一个点的访问极有可能更多次。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值