搜索(BFS、DFS、回溯)

这类题是最简单的了都是一个套路,不像动态规划一类题一个套路,没做过就是不会也极难想出来。

一、BFS

解决的问题:用来初始点解决到指定点的最短路径问题,因为图的每一层上的点到初始点的距离相同。(注意是无权图)

在程序实现 BFS 时需要考虑以下问题:

  • 队列:用来存储每一轮遍历得到的节点;
  • 标记:对于遍历过的节点,应该将它标记,防止重复遍历。

下面这两个题都是比较典型的广度优先题

1091. 二进制矩阵中的最短路径

这题笔者超时了百思不得其解。

class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        if(grid[0][0] == 1 || grid[m-1][n-1] == 1) return -1;
        int[][] dirc = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
        LinkedList<int[]> queue = new LinkedList<>();
        queue.add(new int[]{0,0,1});
        while(!queue.isEmpty()) {
            int[] p = queue.poll();
            if(p[0]==m-1 && p[1]==n-1) return p[2];
            grid[p[0]][p[1]] = 1;
            for(int[] d : dirc) {
                int x = p[0] + d[0];
                int y = p[1] + d[1];
                if(x<0||y<0||x>=m||y>=n||grid[x][y]==1)
                    continue;
                queue.add(new int[]{x,y,p[2]+1});
            }
        }
        return -1;
    }
}

127. 单词接龙

class Solution {
    public int ladderLength(String  d, String endWord, List<String> wordList) {
        int ans = 0;
        if(wordList.size()==0) return ans;
        LinkedList<String> queue = new LinkedList<>();
        boolean[] visited = new boolean[wordList.size()];
        queue.add(d);
        while(!queue.isEmpty()) {
            int size = queue.size();
            ans++;
            while(size-->0) {
                String p = queue.poll();
                for(int i = 0; i < wordList.size(); i++) {
                    String s = wordList.get(i);
                    if(!visited[i] && f(p,s)) {
                        if(s.equals(endWord)) return ++ans;
                        queue.add(s);
                        visited[i] = true;
                    }
                }
            }
        }
        return 0;
    }
    private boolean f(String s1, String s2) {
        int i = 0;
        int j = 0;
        boolean flag = true;
        while(i < s1.length()) {
            if(s1.charAt(i) == s2.charAt(j)) {
                i++;
                j++;
            }else if(s1.charAt(i) != s2.charAt(j)&&flag) {
                flag = false;
                i++;
                j++;
            }else return false;
        }
        return true;
    }
}

二、DFS

解决的问题:用来解决连通性的问题。

在程序实现 DFS 时需要考虑以下问题:

  • 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
  • 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。
  • 判断:进栈出栈都需要判断元素是否已经被访问过,因为栈中有可能出现已经访问过的元素(找个图自己走一下)。

695. 岛屿的最大面积

import javafx.util.Pair;
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int[][] direction = {{-1,0},{1,0},{0,-1},{0,1}};
        int ans = 0;
        for(int i = 0; i < grid.length; i++) {
            for(int j = 0; j < grid[0].length; j++) {
                if(grid[i][j]==1) {
                    int count = 0;
                    LinkedList<Pair<Integer,Integer>> queue = new LinkedList<>();
                    queue.add(new Pair<>(i,j));
                    while(!queue.isEmpty()) {
                        Pair<Integer,Integer> p = queue.pollLast();
                        if(grid[p.getKey()][p.getValue()]==1) {
                            count++;
                            grid[p.getKey()][p.getValue()] = 0;
                            for(int[] d : direction) {
                                int x = p.getKey() + d[0];
                                int y = p.getValue() + d[1];
                                if(x<0||y<0||x>=grid.length||y>=grid[0].length||grid[x][y]==0)
                                    continue;
                                queue.add(new Pair<>(x,y));
                            }
                        }
                    }
                    ans = Math.max(ans,count);
                }
            }
        }
        return ans;
    }
}

130. 被围绕的区域

import javafx.util.Pair;
class Solution {
    public void solve(char[][] board) {
        int[][] direction = {{-1,0},{1,0},{0,-1},{0,1}};
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[0].length; j++) {
                //边界O好处理一些,这里我们选择先找到边界O
                if(board[i][j]=='O'&&(i==0||i==board.length-1||j==0||j==board[0].length-1)) {
                    LinkedList<Pair<Integer,Integer>> queue = new LinkedList<>();
                    queue.add(new Pair<>(i,j));
                    while(!queue.isEmpty()) {
                        Pair<Integer,Integer> p = queue.pollLast();
                        if(board[p.getKey()][p.getValue()]=='O') {
                            board[p.getKey()][p.getValue()] = 'T';
                            for(int[] d : direction) {
                                int x = p.getKey() + d[0];
                                int y = p.getValue() + d[1];
                                if(x<0||y<0||x>=board.length||y>=board[0].length||board[x][y]=='T'||board[x][y]=='X')
                                    continue;
                                queue.add(new Pair<>(x,y));
                            }
                        }
                    }
                }
            }
        }
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[0].length; j++) {
                if(board[i][j]=='O') board[i][j] = 'X';
                if(board[i][j]=='T') board[i][j] = 'O';
            }
        }
    }
}

417. 太平洋大西洋水流问题

class Solution {
    public List<List<Integer>> pacificAtlantic(int[][] matrix) {
        List<List<Integer>> ans = new LinkedList<>();
        if(matrix.length == 0||matrix[0].length == 0) return ans;
        int m = matrix.length;
        int n = matrix[0].length;
        boolean[][] m1 = new boolean[m][n];
        boolean[][] m2 = new boolean[m][n];
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(i==0||j==0) dfs(matrix,new int[]{i,j},m1);
                if(i==m-1||j==n-1) dfs(matrix,new int[]{i,j},m2);
            }
        }
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(m1[i][j]&&m2[i][j])
                    ans.add(Arrays.asList(i,j));
            }
        }
        return ans;
    }
    private void dfs(int[][] matrix, int[] s, boolean[][] m) {
        LinkedList<int[]> stack = new LinkedList<>();
        int[][] dir = {{-1,0},{1,0},{0,-1},{0,1}};
        stack.add(s);
        while(!stack.isEmpty()) {
            int[] p = stack.pollLast();
            if(!m[p[0]][p[1]]) {
                m[p[0]][p[1]] = true;
                for(int[] d : dir) {
                    int x = p[0] + d[0];
                    int y = p[1] + d[1];
                    if(x<0||y<0||x>= matrix.length||y>=matrix[0].length||m[x][y])
                        continue;
                    if(matrix[x][y] >= matrix[p[0]][p[1]])
                        stack.add(new int[]{x,y});
                }
            }
        }
    }
}
View Code

 三、回溯

解决的问题:排列组合问题。

回溯法其实就是一种枚举,把所有的情况都走一遍筛选出符合条件的解。有很多的题都可以用回溯解决,但是效率相差很大。回溯法适合解决,枚举出100种情况,但是有98种情况符合题意的这类问题。但是,如果枚举100种情况,只有1种情况符合题意,显示用回溯效率是极低。

因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题:

  • 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;
  • 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。

17. 电话号码的字母组合

class Solution {
    private HashMap<Integer,String> hash = new HashMap<>();
    private LinkedList<String> ans = new LinkedList<>();
    public List<String> letterCombinations(String digits) {
        hash.put(2,"abc");
        hash.put(3,"def");
        hash.put(4,"ghi");
        hash.put(5,"jkl");
        hash.put(6,"mno");
        hash.put(7,"pqrs");
        hash.put(8,"tuv");
        hash.put(9,"wxyz");
        if(digits == null || digits.length() == 0) return ans;
        backtrace("",digits);
        return ans;
    }
    private void backtrace(String temp, String digits) {
        if(digits.length()==0) {
            ans.add(temp);
        }else {
            Integer key = Integer.valueOf(digits.substring(0,1));
            String s = hash.get(key);
            for(int i = 0; i < s.length(); i++) {
                temp = temp + s.substring(i,i+1);
                backtrace(temp,digits.substring(1));
                temp = temp.substring(0,temp.length()-1);
            }
        }
    }
}

93. 复原IP地址

class Solution {
    List<String> ans = new LinkedList<>();
    public List<String> restoreIpAddresses(String s) {
        backtrace(0,s,"");
        return ans;
    }
    private void backtrace(int k, String s, String temp) {
        if(k==4||s.length()==0) {
            if(k==4&&s.length()==0) ans.add(temp);
            return;
        }else {
            for(int i = 0;i < s.length() &&i <= 2; i++) {
                if(s.charAt(0)=='0'&&i!=0) break;
                String part = s.substring(0,i+1);
                if(Integer.valueOf(part)<=255) {
                    if(temp.length()!=0) {
                        part = '.' + part;
                    }
                    temp = temp + part;
                    backtrace(k+1,s.substring(i+1),temp);
                    temp = temp.substring(0,temp.length()-part.length());
                }
            }
        }
    }
}

37. 解数独

class Solution {
    private boolean[][] m = new boolean[9][10];
    private boolean[][] n = new boolean[9][10];
    private boolean[][] k = new boolean[9][10];
    private char[][] b;
    public void solveSudoku(char[][] board) {
        b = 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';
                    m[i][num] = true;
                    n[j][num] = true;
                    k[(i/3)*3+j/3][num] = true;
                }
            }
        }
        backtrace(0,0);
    }
    private boolean backtrace(int r, int c) {
        if(r < 8 && c == 9) {
            r++;
            c = 0;
        }
        if(r == 8 && c == 9) return true;
        if(b[r][c] == '.') {
          for(int i = 1; i <= 9; i++) {
              int x = (r/3)*3+c/3;
              if(m[r][i]||n[c][i]||k[x][i]) continue;
              b[r][c] = (char)('0'+i);
              m[r][i] = true;
              n[c][i] = true;
              k[x][i] = true;
              if(backtrace(r,c+1)) return true;
              b[r][c] = '.';
              m[r][i] = false;
              n[c][i] = false;
              k[x][i] = false;
          }  
        }else return backtrace(r,c+1);
        return false;
    }
}

51. N皇后

class Solution {
    List<List<String>> list = new LinkedList<>();
    public List<List<String>> solveNQueens(int n) {
        boolean[] c = new boolean[n+1];
        boolean[][] b = new boolean[n+1][n+1];
        backtrace(1,n,c,b);
        return list;
    }
    private void backtrace(int k, int n, boolean[] c, boolean[][] b) {
        if(k == n+1) {
            fill(b,n);
            return;
        }
        for(int i = 1; i <= n; i++) {
            if(c[i]||isHou(k,i,n,b)) continue;
            b[k][i] = true;
            c[i] = true;
            backtrace(k+1,n,c,b);
            b[k][i] = false;
            c[i] = false;
        }
    }
    private void fill(boolean[][] b, int n) {
        LinkedList<String> temp = new LinkedList<>();
        for(int i = 1; i <= n; i++) {
            StringBuilder s = new StringBuilder();
            for(int j = 1; j <= n; j++) {
                if(b[i][j]) s.append('Q');
                else s.append('.');
            }
            temp.add(new String(s));
        }
        list.add(temp);
    }
    private boolean isHou(int x, int y, int n, boolean[][] b) {
        int i = x;
        int j = y;
        while(i<=n&&j<=n) {
            if(b[i++][j++]) return true;
        }
        i = x;
        j = y;
        while(i>=1&&j>=1) {
            if(b[i--][j--]) return true;
        }
        i = x;
        j = y;
        while(i>=1&&j<=n) {
            if(b[i--][j++]) return true;
        }
        i = x;
        j = y;
        while(i<=n&&j>=1) {
            if(b[i++][j--]) return true;
        }
        return false;
    }
}

 

posted @ 2019-12-03 10:49  卑微芒果  Views(928)  Comments(0Edit  收藏  举报