【递归、搜索与回溯】优美的排列,N皇后,有效的数独,解数独,单词搜索,黄金矿工,不同路径III

文章目录

1. 优美的排列(LC526)

优美的排列

题目描述

解题思路

类似全排列,排列同时要检查是否符合题意,使用check数组标记已使用的元素。

代码实现

java 复制代码
class Solution {
    boolean[] check ;
    int ret;
    public int countArrangement(int n) {
        check = new boolean[n+1];
        dfs(1,n);
        return ret;
    }
    void dfs(int i ,int n){
        if(i == n+1){
            ret++;
            return;
        }
        for(int j = 1;j<=n;j++){
            if(!check[j] && (i%j==0 || j%i==0)){
                check[j] = true;
                dfs(i+1,n);
                check[j] = false;
            }
        }
    }
}

2. N皇后(LC51)

N皇后

题目描述

解题思路

遍历行时利用三个数组(简化哈希表)剪枝:

  • col[] ,在遍历行时同时记录所在的列,如果当前位置所在列被标记,就剪枝;
  • dig1[] ,记录主对角线。转换为一次函数,y-x是定值b,为了防止b越界,整体向上平移n个单位。
  • dig1[] ,记录副对角线。y+x是定值b

代码实现

java 复制代码
boolean[] col;
    boolean[] dig1;
    boolean[] dig2;
    char[][] path;
    List<List<String>> ret = new ArrayList<>();
    StringBuilder str = new StringBuilder();

    public List<List<String>> solveNQueens(int n) {
        col = new boolean[n];
        dig1 = new boolean[2*n];
        dig2 = new boolean[2*n];
        path = new char[n][n];

        for(int i = 0;i<n;i++){
            for(int j = 0;j<n;j++){
                path[i][j] = '.';
            }
        }

        dfs(0,n);
        return ret;
    }
    void dfs(int i,int n){
        if(i == n){
            //此时遍历结束,把每一行转为list<String>
            List<String> tmp = new ArrayList<>();
            for(int k = 0;k<n;k++)
                tmp.add(new String(path[k]));
            ret.add(new ArrayList<>(tmp));
            return;
        }

        // i是纵坐标,j是横坐标
        for(int j = 0;j <n;j++){
            if(!(col[j] || dig1[i - j + n ] || dig2[j+i])){
                path[i][j] = 'Q';
                col[j] = true;
                dig1[i - j + n] = true;
                dig2[i + j] = true;
                dfs(i + 1,n);

                //恢复现场
                col[j] = false;
                dig1[i - j + n] = false;
                dig2[i + j] = false;
                path[i][j] = '.';
            }
        }
    }

3. 有效的数独(LC36)

有效的数独

题目描述

解题思路

定义三个数组分别标记当前行,当前列和当前九宫格中1-9是否被使用过

  • row[][]col[][] 第一个元素表示横坐标或纵坐标,第二个元素表示1-9是否被使用
  • grid[][][] 前两个元素表示横纵坐标(计算方式:题目中的二维数组坐标分别/3,得到grid数组对应的坐标)第三个元素表示1-9是否被占用

代码实现

java 复制代码
class Solution {
    boolean[][] row = new boolean[9][10];
    boolean[][] col = new boolean[9][10];       
    boolean[][][] grid = new boolean[3][3][10];

    public boolean isValidSudoku(char[][] board) {
        for(int i = 0;i<9;i++){
            for(int j = 0;j<9;j++){
                if(board[i][j] != '.'){
                    int num = board[i][j] - '0';
                    if(row[i][num]||col[j][num]||grid[i/3][j/3][num])
                        return false;
                    else{
                        row[i][num] = true;
                        col[j][num] = true;
                        grid[i/3][j/3][num] = true;
                    }
                }
            }
        }
        return true;
    }
}

4. 解数独(LC37)

解数独

题目描述

解题思路

与上一题的思路类似,利用三个数组帮助记录数字是否被使用过,使用过可以直接剪枝。

如图所示,可能存在第一个空填1,后面没有正确答案的情况。因此这个函数需要一个返回值,没有正确答案返回false。

代码实现

java 复制代码
class Solution {
    boolean[][] row = new boolean[9][10];
    boolean[][] col = new boolean[9][10];
    boolean[][][] grid = new boolean[3][3][10];
    public void solveSudoku(char[][] board) {
        //初始化
        for(int i =0;i<9;i++){
            for(int j = 0;j<9;j++){
                if(board[i][j]!='.'){
                    int num = board[i][j]-'0';
                    row[i][num] = col[j][num] =grid[i/3][j/3][num] = true;
                }
            }
        }
        dfs(board);
    }
    boolean dfs(char[][] board){
        for(int i = 0;i<9;i++){
            for(int j = 0;j<9;j++){
                //剪枝 是数字则直接跳过,是空则进入
                if(board[i][j]=='.'){
                    for(int k =1;k<=9;k++){
                        //填空
                        if(!(row[i][k]||col[j][k]||grid[i/3][j/3][k])){
                            board[i][j] =(char)('0' + k);
                            row[i][k] = col[j][k] = grid[i/3][j/3][k] = true;
                            if(dfs(board)) 
                            		return true;

                            //填错了 恢复现场
                            board[i][j] = '.';
                            row[i][k] = col[j][k] = grid[i/3][j/3][k] = false;

                        }
                    }
                    //1-9没有可以填入的,说明失败
                    return false;
                }
            }
        }
        //是数字,所以没有进入for循环
        return true;
    }
}

5. 单词搜索(LC79)

单词搜索

题目描述

解题思路

类似于"走迷宫",入口是单词首字母,出口是单词尾字母。因此首先要找到首字母。以字母为中心向四周搜索。

函数:boolean dfs( int i, int j ,int pos),传入字母在数组的位置,下一个字母在字符串的位置。返回值说明当前路径是否能找到单词。二维数组和单词转换为全局变量

注意: 在二维数组中走迷宫要警惕"走重路"的情况。可以新建一个二维数组记录是否经过;也可以直接修改原数组(风险比较大,尽量避免)。

代码实现

java 复制代码
class Solution {
    boolean[][] check;
    char[][] board;
    String word;
    int m;
    int n;
    public boolean exist(char[][] _board, String _word) {
        m = _board.length;
        n = _board[0].length;
        check = new boolean[m][n];
        board = _board;
        word = _word;
        //找入口
        char tar = word.charAt(0);
        for(int i = 0;i<m;i++){
            for(int j = 0;j<n;j++){
                if(board[i][j]==tar){
                    check[i][j] = true;
                    if(dfs(i,j,1))
                        return true;
                    check[i][j] = false;
                }
            }
        }
        return false;
    }
    
    boolean dfs(int i, int j, int pos){
        if(pos==word.length())
            return true;

        char tar = word.charAt(pos);
        if(i>0 && !check[i-1][j] && board[i-1][j] == tar){
            check[i-1][j] = true;
            if(dfs(i-1,j,pos+1))
                return true;
            check[i-1][j] = false;
        }

        if(i<m-1 && !check[i+1][j] && board[i+1][j] == tar){
            check[i+1][j] = true;
            if(dfs(i+1,j,pos+1))
                return true;
            check[i+1][j] = false;        
         }

        if(j>0 && !check[i][j-1] && board[i][j-1] == tar){
            check[i][j-1] = true;
            if(dfs(i,j-1,pos+1))
                return true;
            check[i][j-1] = false;        
         }

        if(j<n-1 && !check[i][j+1] && board[i][j+1] == tar){
            check[i][j+1] = true;
            if(dfs(i,j+1,pos+1))
                return true;
            check[i][j+1] = false;
        }
        return false;
    }
}
  • 优化:利用向量数组搞定四个方向
java 复制代码
        int[] dx = {1,-1,0,0};
        int[] dy = {0,0,1,-1};

        for(int k = 0;k<4;k++){
            int x = i+dx[k];
            int y = j+dy[k];
            if(x>=0 && x<m && y>=0 && y<n && !check[x][y] && board[x][y] == tar){
                check[x][y] = true;
                if(dfs(x,y,pos+1))
                    return true;
                check[x][y] = false;
            }
        }

6. 黄金矿工(LC1219)

黄金矿工

题目描述

解题思路

与上一题相似,因为需要遍历所有情况,递归函数不需要提前返回,不需要设置返回值。

代码实现

java 复制代码
class Solution {
    int[][] grid;
    int m ;
    int n;
    boolean[][] check;
    int ret = 0;

    public int getMaximumGold(int[][] _grid) {
        m = _grid.length;
        n = _grid[0].length;
        grid = _grid;
        check = new boolean[m][n];
        for(int i = 0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]!=0){
                    check[i][j] = true;
                    dfs(i,j,0);
                    //恢复现场
                    check[i][j] = false;
                }
            }
        }
        return ret;
    }

    void dfs(int i ,int j,int sum){
        sum += grid[i][j];
        if(sum > ret)
            ret = sum;
        int[] dx = {1,-1,0,0};
        int[] dy = {0,0,1,-1};

        for(int k = 0;k<4;k++){
            int x = dx[k]+i;
            int y = dy[k]+j;

            if(x>=0 && x < m && y >= 0 && y < n && !check[x][y] && grid[x][y]!=0){
                check[x][y] = true;
                dfs(x,y,sum);
                //恢复现场
                check[x][y] = false;
            }
        }
    }
}

7. 不同路径III(LC980)

不同路径III

题目描述

解题思路

与前两个题类似。需要注意的是:要提前统计0的个数,保证最终不漏下格子。

代码实现

java 复制代码
class Solution {
    int[][] grid;
    int m = 0;
    int n = 0;
    int step = 0;
    boolean[][] check;
    int ret = 0;
    public int uniquePathsIII(int[][] _grid) {
        grid = _grid;
        m = _grid.length;
        n = _grid[0].length;
        check = new boolean [m][n];

        //统计总共要走的步数和起点
        int bx = 0;
        int by = 0;
        for(int i = 0;i<m;i++){
            for(int j = 0;j<n;j++){
                if(grid[i][j] == 1){
                    bx = i;
                    by = j;
                }else if(grid[i][j] == 0)    
                    step++;
            }
        }
        check[bx][by] = true;
        step += 2;
        dfs(bx,by,1);  
        return ret;
    }
    void dfs(int i ,int j,int walked){
        if(grid[i][j] == 2 ){
            if(step == walked)
                ret++;
            return;
        }
        int[] dx = {0,0,1,-1};
        int[] dy = {1,-1,0,0};

        for(int k = 0;k<4;k++){
            int x = i+dx[k];
            int y = j+dy[k];
            if(x>=0 && y>=0 && x<m && y<n && !check[x][y] && grid[x][y] != -1){
                check[x][y] = true;
                dfs(x,y,walked+1);
                check[x][y] = false;
            }
        }
    }
}
相关推荐
ejjdhdjdjdjdjjsl1 小时前
halcon算子
人工智能·算法·计算机视觉
Aawy1202 小时前
C++与Rust交互编程
开发语言·c++·算法
某林2122 小时前
主流 3D SLAM 算法核心架构深度解析:VINS、ORB-SLAM3 与 FAST-LIO
算法·3d·架构
做一个码农都是奢望2 小时前
计算机控制系统:最小拍控制系统设计入门
数据结构·算法
米粒12 小时前
力扣算法刷题 Day 16
算法·leetcode·职场和发展
重生之后端学习2 小时前
31. 下一个排列
数据结构·算法·leetcode·职场和发展·排序算法·深度优先
Frostnova丶2 小时前
LeetCode 3212. 统计X和Y出现次数相等的子矩阵数量
算法·leetcode·矩阵
We་ct2 小时前
LeetCode 53. 最大子数组和:两种高效解法(动态规划+分治)
前端·算法·leetcode·typescript·动态规划·分治