经典算法题型(二):二维数组(平面地图)的递归操作

一、基本概念

1.在算法中有一类题型经常出现,通常题目给出一个二维的数组,让你求出有多少条路径?有多少个岛屿?

甚至有些题目给出二维字符数组,让你寻找是否存在某个单词。

这些问题都涉及到递归回溯的相关知识,在此通过leetcode 79. Word Search,和leetcode 200. Number of Islands来进行总结分析。

二、第79题

1.题目:

79. 单词搜索

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例:

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false.

2.代码及注释:

public class Solution {

    private int d[][] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};//这四个数组定义了X和Y可能的移动方向
    private int m, n;
    private boolean[][] visited;//标记这个字母已经走过,不能再重复走

    public boolean exist(char[][] board, String word) {

        if(board == null || word == null)
            throw new IllegalArgumentException("board or word can not be null!");

        m = board.length;
        if(m == 0)
            throw new IllegalArgumentException("board can not be empty.");
        n = board[0].length;
        if(n == 0)
            throw new IllegalArgumentException("board can not be empty.");

        visited = new boolean[m][n];
        //每次选择不同的起点,并进行寻路,任何一个起点找到word即可返回true,如果所有起点都找不到,则返回false.
        for(int i = 0 ; i < m ; i ++)
            for(int j = 0 ; j < n ; j ++)
                if(searchWord(board, word, 0, i, j))
                    return true;

        return false;
    }

    //这个函数用来判断向四个方向移动后的x,y是否越界
    private boolean inArea( int x , int y ){
        return x >= 0 && x < m && y >= 0 && y < n;
    }

    // 从board[startx][starty]开始, 寻找word[index...word.size())
    private boolean searchWord(char[][] board, String word, int index,
                               int startx, int starty){

        //assert(inArea(startx,starty));
        if(index == word.length() - 1)
            return board[startx][starty] == word.charAt(index);

        if(board[startx][starty] == word.charAt(index)){
            visited[startx][starty] = true;
            // 从startx, starty出发,向四个方向寻
            for(int i = 0 ; i < 4 ; i ++){
                int newx = startx + d[i][0];
                int newy = starty + d[i][1];
                if(inArea(newx, newy) && !visited[newx][newy] &&
                        searchWord(board, word, index + 1, newx, newy))
                    return true;
            }
            visited[startx][starty] = false;//某条路径走完,返回上一层走别的路之前都要将visited[][]置为false。
        }
        return false;
    }
//  以下为测试函数
    // public static void main(String args[]){

    //     char[][] b1 = { {'A','B','C','E'},
    //                     {'S','F','C','S'},
    //                     {'A','D','E','E'}};

    //     String words[] = {"ABCCED", "SEE", "ABCB" };
    //     for(int i = 0 ; i < words.length ; i ++)
    //         if((new Solution()).exist(b1, words[i]))
    //             System.out.println("found " + words[i]);
    //         else
    //             System.out.println("can not found " + words[i]);

    //     // ---

    //     char[][] b2 = {{'A'}};
    //     if((new Solution()).exist(b2,"AB"))
    //         System.out.println("found AB");
    //     else
    //         System.out.println("can not found AB");
    // }
}

三、第200题

1.题目:

200. 岛屿数量

给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

示例 1:

输入:
11110
11010
11000
00000

输出: 1

示例 2:

输入:
11000
11000
00100
00011

输出: 3

2.代码及注释:

class Solution {

    private int d[][] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    private int m, n;
    private boolean visited[][];

    public int numIslands(char[][] grid) {

        if(grid == null || grid.length == 0 || grid[0].length == 0)
            return 0;

        m = grid.length;
        n = grid[0].length;

        visited = new boolean[m][n];

        int res = 0;
        for(int i = 0 ; i < m ; i ++)
            for(int j = 0 ; j < n ; j ++)
                if(grid[i][j] == '1' && !visited[i][j]){
                    dfs(grid, i, j);//本次求解的思路类似于深度优先遍历
                    res ++;
                }

        return res;
    }

    // 从grid[x][y]的位置开始,进行floodfill
    // 保证(x,y)合法,且grid[x][y]是没有被访问过的陆地
    private void dfs(char[][] grid, int x, int y){

        //assert(inArea(x,y));
        visited[x][y] = true;
        for(int i = 0; i < 4; i ++){
            int newx = x + d[i][0];
            int newy = y + d[i][1];
            if(inArea(newx, newy) && !visited[newx][newy] && grid[newx][newy] == '1')
                dfs(grid, newx, newy);
        //此处与上一题最大的不同在于:visited[][]无需置为false。
        }

        return;
    }

    private boolean inArea(int x, int y){
        return x >= 0 && x < m && y >= 0 && y < n;
    }
    //以下为测试函数:
    // public static void main(String[] args) {

    //     char grid1[][] = {
    //         {'1','1','1','1','0'},
    //         {'1','1','0','1','0'},
    //         {'1','1','0','0','0'},
    //         {'0','0','0','0','0'}
    //     };
    //     System.out.println((new Solution()).numIslands(grid1));
    //     // 1

    //     // ---

    //     char grid2[][] = {
    //         {'1','1','0','0','0'},
    //         {'1','1','0','0','0'},
    //         {'0','0','1','0','0'},
    //         {'0','0','0','1','1'}
    //     };
    //     System.out.println((new Solution()).numIslands(grid2));
    //     // 3
    // }
}

四、总结

通过以上两道题目,可以看出二维数组的寻路、寻词,基本方法就是使用递归回溯,可以简单的理解为试探性前进。以当前起点深入,向下深入,直到到达最后一个位置。如果向下深入发现路已经堵死了,则可以返回上一层,换另一条路。

通常而言,递归回溯的方法都十分好用,但是如果遇到数据量比较大的二维数组,可能运行的时间就会超过预估的时间,这时还要进行适当的剪枝甚至使用动态规划的思维。

之后我还会继续学习,关于动态规划的部分也会成为接下去自己学习的一个重点,加油[< _ >]!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IMUHERO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值