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

一、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});
}
}
}
}
}

三、回溯

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

回溯法其实就是一种枚举,把所有的情况都走一遍筛选出符合条件的解。有很多的题都可以用回溯解决,但是效率相差很大。回溯法适合解决,枚举出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;
}
}
05-20 23:28