BAT手撕算法题·深度优先遍历
深度优先遍历(dfs,deep first search)的思想最早来源于图论,和宽度优先遍历组成了两大遍历算法,但他们有不同的应用场景。本文从lc中的几道经典面试常问深度优先遍历问题入手,教你一步搞定这类问题。
一.什么是深度优先遍历
class Solution {// 定义方向数组private int[][] directed = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};private boolean[][] visited;private char[][] board;private String word;private int rows;private int cols;public boolean exist(char[][] board, String word) {if (board == null || word == null || board.length == 0 || word.trim().length() == 0) {return false;}// 初始化全局变量rows = board.length;cols = board[0].length;visited = new boolean[rows][cols];this.board = board;this.word = word;// 在每一个点开始深度优先遍历,直到找到为止,找不到返回falsefor (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {if (dfs(i, j, 0)) {return true;}}}return false;}private boolean dfs(int i, int j, int cur) {// dfs搜索结束条件if (cur == word.length() - 1) {return word.charAt(cur) == board[i][j];}// 在符合条件的位置深度优先遍历if (word.charAt(cur) == board[i][j]) {// 标示该节点被访问到visited[i][j] = true;for (int k = 0; k < 4; k++) {int newI = i + directed[k][0];int newJ = j + directed[k][1];if (isArea(newI, newJ) && !visited[newI][newJ]) {if (dfs(newI, newJ, cur + 1)) {return true;}}}// 将该节点dfs搜索过的全部节点状态重置,以防干扰到下一轮的搜索visited[i][j] = false;}return false;}private boolean isArea(int x, int y) {return x >= 0 && x < rows && y >= 0 && y < cols;}}
class Solution {// . x, y + 1// .x-1, y x, y x + 1, y// . x. y - 1private int[][] directed = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};private char[][] grid;private boolean[][] visited;private int rows;private int cols;private int res;public int numIslands(char[][] grid) {// 求图的连通分量类似,先对节点进行dfs,count + 1,每次dfs到的即标记;if (grid == null || grid.length == 0) {return 0;}this.grid = grid;rows = grid.length;cols = grid[0].length;visited = new boolean[rows][cols];for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {// 从第一个遍历到的没被标记过的陆地开始dfs搜索标记if (grid[i][j] == '1' && !visited[i][j]) {dfs(i, j);res++;}}}return res;}private void dfs(int i, int j) {// 访问标记visited[i][j] = true;// 上下左右进行搜索for (int k = 0; k < 4; k++) {int newI = i + directed[k][0];int newJ = j + directed[k][1];// 是陆地且没被访问过才继续dfsboolean flag = newI >= 0 && newI < rows && newJ >= 0 && newJ < cols && !visited[newI][newJ] && grid[newI][newJ] == '1';if (flag) {dfs(newI, newJ);}}}}
class Solution {private int[][] directed = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};private char[][] board;private int rows;private int cols;public void solve(char[][] board) {// 通过dfs求出边界的且为O的连通分量,把它标记为!,之后再遍历一遍board,// 将O置为X,将与边相连的连通分量设回Oif (board == null || board.length == 0) {return;}rows = board.length;cols = board[0].length;this.board = board;// 找到边界O节点进行深度优先遍历,将所有与边界O节点连通的O节点设为!for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {boolean flag = board[i][j] == 'O' && (i == 0 || i == rows - 1 || j == 0 || j == cols - 1);if (flag) {dfs(i, j);}}}// 将O设置为X,将被标示为!的节点设置为Ofor (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {if (board[i][j] == 'O') {board[i][j] = 'X';} else if (board[i][j] == '!') {board[i][j] = 'O';}}}}private void dfs(int i, int j) {board[i][j] = '!';// 继续dfsfor (int k = 0; k < 4; k++) {int newI = i + directed[k][0];int newJ = j + directed[k][1];boolean isArea = newI >= 0 && newI < rows && newJ >= 0 && newJ < cols;if (isArea && board[newI][newJ] == 'O') {dfs(newI, newJ);}}}}
// . x, y + 1// .x-1, y x, y x + 1, y// . x. y - 1private int[][] directed = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};private char[][] grid;private boolean[][] visited;private int res;public int numIslands(char[][] grid) {for (int i = 0; i < grid.length; i++) {dfs(i, j);}return res;}private void dfs(int row, int col) {// 访问visited[row][col] = true;// 上下左右进行搜索for (int i = 0; i < 4; i++) {int newRow = row + directed[i][0];int newCol = col + directed[i][1];// 剪枝if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && marked[newRow][newCol] == false && grid[newRow][newCol] == '1') {dfs(newRow, newCol, rows, cols);}}return;}
