文章目录
树
N叉树的层序遍历
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> ret = new ArrayList<>();
if(root == null) return ret;
Queue<Node> q = new LinkedList<>();
q.add(root);
while(!q.isEmpty()) {
int sz = q.size();
List<Integer> tmp = new ArrayList<>();//统计本层的节点信息
for(int i = 0; i < sz; i++) {
Node t = q.poll();
tmp.add(t.val);
//让孩子入队
for(Node child: t.children) {
if(child != null)
q.add(child);
}
}
ret.add(tmp);
}
return ret;
}
}
二叉树的锯齿形层序遍历
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
if(root == null) return ret;
Queue<TreeNode> q = new ArrayDeque<>();
q.add(root);
int level = 1;
while(!q.isEmpty()) {
int sz = q.size();
List<Integer> tmp = new ArrayList<>();
for(int i = 0; i < sz; i++) {
TreeNode t = q.poll();
tmp.add(t.val);
if(t.left != null) q.add(t.left);
if(t.right != null) q.add(t.right);
}
//判断是否逆序
if(level % 2 == 0) Collections.reverse(tmp);
ret.add(tmp);
level++;
}
return ret;
}
}
二叉树最大宽度
class Solution {
public int widthOfBinaryTree(TreeNode root) {
List<Pair<TreeNode, Integer>> q = new ArrayList<>();//用数组模拟队列
q.add(new Pair<TreeNode, Integer>(root, 1));
int ret = 0;//记录最终结果
while(!q.isEmpty()) {
//更新这一层的宽度
Pair<TreeNode, Integer> t1 = q.get(0);
Pair<TreeNode, Integer> t2 = q.get(q.size() - 1);
ret = Math.max(ret, t2.getValue() - t1.getValue() + 1);
//让下一层进队
List<Pair<TreeNode, Integer>> tmp = new ArrayList<>();
for(Pair<TreeNode, Integer> t: q) {
TreeNode node = t.getKey();
int index = t.getValue();
if(node.left != null) {
tmp.add(new Pair<TreeNode, Integer>(node.left, index * 2));
}
if(node.right != null) {
tmp.add(new Pair<TreeNode, Integer>(node.right, index * 2 + 1));
}
}
q = tmp;
}
return ret;
}
}
在每个树行中找最大值
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> ret = new ArrayList<>();
if(root == null) return ret;
Queue<TreeNode> q = new LinkedList<>();
q.add(root);
while(!q.isEmpty()) {
int sz = q.size();
int tmp = Integer.MIN_VALUE;
for(int i = 0; i < sz; i++) {
TreeNode t = q.poll();
tmp = Math.max(tmp, t.val);
if(t.left != null) q.add(t.left);
if(t.right != null) q.add(t.right);
}
ret.add(tmp);
}
return ret;
}
}
FloodFill算法
图像渲染
class Solution {
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
int prev = image[sr][sc];//刚开始的颜色
if(prev == color) return image;//处理特殊情况
int m = image.length, n = image[0].length;
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{sr, sc});
while(!queue.isEmpty()) {
int[] t = queue.poll();
int a = t[0], b = t[1];
image[a][b] = color;
//上下左右四个方向
for(int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if(x >= 0 && x <m && y >= 0 && y < n && image[x][y] == prev) {
queue.add(new int[]{x, y});
}
}
}
return image;
}
}
岛屿数量
class Solution {
int[] dx = {0, 0, -1, 1};
int[] dy = {1, -1, 0, 0};
boolean[][] vis= new boolean[301][301];
int m, n;
public int numIslands(char[][] grid) {
m = grid.length; n = grid[0].length;
int ret = 0;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(grid[i][j] == '1' && !vis[i][j]) {
ret++;
bfs(grid, i, j);
}
}
}
return ret;
}
void bfs(char[][] grid, int i, int j) {
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{i, j});
vis[i][j] = true;
while(!queue.isEmpty()) {
int[] t = queue.poll();
int a = t[0], b = t[1];
for(int k = 0; k < 4; k++) {
int x = a + dx[k],y = b + dy[k];
if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1' && !vis[x][y]) {
queue.add(new int[]{x, y});
vis[x][y] = true;
}
}
}
}
}
岛屿的最大面积
class Solution {
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
boolean[][] vis = new boolean[51][51];
int m, n;
public int maxAreaOfIsland(int[][] grid) {
m = grid.length; n = grid[0].length;
int ret = 0;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(grid[i][j] == 1 && !vis[i][j]) {
ret = Math.max(ret, bfs(grid, i, j));
}
}
}
return ret;
}
int bfs(int[][] grid, int i, int j) {
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{i, j});
vis[i][j] = true;
int count = 1;
while(!queue.isEmpty()) {
int[] t = queue.poll();
int a = t[0], b = t[1];
for(int k = 0; k < 4; k++) {
int x = a + dx[k], y = b + dy[k];
if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && !vis[x][y]) {
queue.offer(new int[]{x, y});
vis[x][y] = true;
count++;
}
}
}
return count;
}
}
被围绕的区域
算法思路:
正难则反。
class Solution {
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
int m, n;
public void solve(char[][] board) {
m = board.length;
n = board[0].length;
//1.处理边界的'O'联通块,全修改成'.'
for(int j = 0; j < n; j++) {
if(board[0][j] == 'O') bfs(board, 0 ,j);
if(board[m - 1][j] == 'O') bfs(board, m - 1, j);
}
for(int i = 1; i < m - 1; i++) {
if(board[i][0] == 'O') bfs(board, i, 0);
if(board[i][n - 1] == 'O') bfs(board, i, n -1);
}
//2.还原
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
if(board[i][j] == 'O') board[i][j] = 'X';
else if(board[i][j] == '.') board[i][j] = 'O';
}
void bfs(char[][] board, int i, int j) {
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{i, j});
board[i][j] = '.';
while(!queue.isEmpty()) {
int[] t = queue.poll();
int a = t[0], b = t[1];
for(int k = 0; k < 4; k++) {
int x = a + dx[k], y = b + dy[k];
if(x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O') {
board[x][y] = '.';
queue.add(new int[]{x, y});
}
}
}
}
}
最短路径问题
迷宫中离入口最近的出口
class Solution {
int[] dx = {0, 0, -1, 1};
int[] dy = {1, -1, 0, 0};
public int nearestExit(char[][] maze, int[] entrance) {
int m = maze.length, n = maze[0].length;
boolean[][] vis = new boolean[m][n];
Queue<int[]> q = new LinkedList<>();
q.add(entrance);
vis[entrance[0]][entrance[1]] = true;
int step = 0;
while(!q.isEmpty()) {
step++;
int sz = q.size();
for(int i = 0; i < sz; i++) {
int[] t = q.poll();
int a = t[0], b = t[1];
for(int j = 0; j < 4; j++) {
int x = a + dx[j], y = b + dy[j];
if(x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == '.' && !vis[x][y]) {
//判断是否已经走到出口
if(x == 0 || x == m - 1 || y == 0 || y == n-1) return step;
q.add(new int[]{x, y});
vis[x][y] = true;
}
}
}
}
return -1;
}
}
走迷宫
import java.util.*;
public class Main {
static int[] dx = {1, -1, 0, 0};
static int[] dy = {0, 0, 1, -1};
static int n, m, x1, x2, y1, y2;
static char[][] arr;
static int[][] vis;//标记当前位置有没有被搜索过,以及走到该位置的最短步数
static int bfs() {
if (arr[x2][y2] == '*') return -1;
Queue<int[]> q = new LinkedList<>();
q.add(new int[] {x1, y1});
while (!q.isEmpty()) {
int[] t = q.poll();
int a = t[0], b = t[1];
for (int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if (x >= 1 && x <= n && y >= 1 && y <= m && arr[x][y] == '.' &&
vis[x][y] == 0) {
q.add(new int[] {x, y});
vis[x][y] = vis[a][b] + 1;
if (x == x2 && y == y2) return vis[x][y];
}
}
}
return -1;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
m = in.nextInt();
x1 = in.nextInt();
y1 = in.nextInt();
x2 = in.nextInt();
y2 = in.nextInt();
arr = new char[n+1][m+1];
vis = new int[n+1][m+1];
for (int i = 1; i <= n; i++) {
String tmp = in.next();
for (int j = 1; j <= m; j++) {
arr[i][j] = tmp.charAt(j - 1);
}
}
System.out.print(bfs());
}
}
最小基因变化
class Solution {
public int minMutation(String startGene, String endGene, String[] bank) {
Set<String> vis = new HashSet<>();//用来标记已经搜索过的状态
Set<String> hash = new HashSet<>();//用来统计基因库里面的字符串
for(String s: bank) hash.add(s);
char[] change = {'A', 'C', 'G', 'T'};
if(startGene.equals(endGene)) return 0;
if(!hash.contains(endGene)) return -1;
Queue<String> q = new LinkedList<>();
q.add(startGene);
vis.add(startGene);
int step = 0;
while(!q.isEmpty()) {
step++;
int sz = q.size();
while(sz-- != 0) {
String t = q.poll();
for(int i = 0; i < 8; i++) {
for(int j = 0; j < 4; j++) {
char[] tmp = t.toCharArray();
tmp[i] = change[j];
String next = new String(tmp);
if(hash.contains(next) && !vis.contains(next)) {
if(next.equals(endGene)) return step;
q.add(next);
vis.add(next);
}
}
}
}
}
return -1;
}
}
单词接龙
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Set<String> hash = new HashSet<>();//用来统计wordList里面的字符串
for(String s: wordList) hash.add(s);
Set<String> vis = new HashSet<>();//标记已经搜素过的单词
if(!hash.contains(endWord)) return 0;
Queue<String> q = new LinkedList<>();
q.add(beginWord);
vis.add(beginWord);
int ret = 1;
while(!q.isEmpty()) {
ret++;
int sz = q.size();
while(sz-- != 0) {
String t = q.poll();
for(int i = 0; i < t.length(); i++) {
for(char ch = 'a'; ch <= 'z'; ch++) {
char[] tmp = t.toCharArray();
tmp[i] = ch;
String next = new String(tmp);
if(hash.contains(next) && !vis.contains(next)) {
if(next.equals(endWord)) return ret;
q.add(next);
vis.add(next);
}
}
}
}
}
return 0;
}
}
为高尔夫比赛砍树
注意:需要按照树的高度从低向高砍掉所有的树
算法思路:
- 先找出砍树的顺序;
- 然后按照砍树的顺序,⼀个⼀个的⽤ bfs 求出最短路即可。
class Solution {
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
int m, n;
public int cutOffTree(List<List<Integer>> forest) {
m = forest.size();
n = forest.get(0).size();
//1.准备工作:确定砍树顺序
List<int[]> trees = new ArrayList<>();
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
if(forest.get(i).get(j) > 1)
trees.add(new int[]{i, j});
Collections.sort(trees, (a, b) -> {
return forest.get(a[0]).get(a[1]) - forest.get(b[0]).get(b[1]);
});
//2.按照顺序砍树
int ret = 0;
int bx = 0, by = 0;
for(int[] tree: trees) {
int x = tree[0], y = tree[1];
int step = bfs(forest, bx, by, x, y);
if(step == -1) return -1;
ret += step;
bx = x;
by = y;
}
return ret;
}
int bfs(List<List<Integer>> forest, int bx, int by, int ex, int ey) {
if(bx == ex && by == ey) return 0;
Queue<int[]> q = new LinkedList<>();
boolean[][] vis = new boolean[m][n];
q.add(new int[]{bx, by});
vis[bx][by] = true;
int step = 0;
while(!q.isEmpty()) {
step++;
int sz = q.size();
while(sz-- != 0) {
int[] t = q.poll();
int a = t[0], b = t[1];
for(int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if(x >= 0 && y >= 0 && x < m && y < n && forest.get(x).get(y) != 0 && !vis[x][y]) {
if(x == ex && y == ey) return step;
q.add(new int[]{x, y});
vis[x][y] = true;
}
}
}
}
return -1;
}
}
多源BFS
矩阵
class Solution {
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
public int[][] updateMatrix(int[][] mat) {
int m = mat.length, n = mat[0].length;
//dist[i][j] == -1; 标记当前位置没有被搜素过
//dist[i][j] != -1; 存的是最短路径
int[][] dist = new int[m][n];
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
dist[i][j] = -1;
Queue<int[]> q = new LinkedList<>();
//1.把所有的0加入到队列里面
for(int i = 0;i < m; i++)
for(int j = 0; j < n; j++) {
if(mat[i][j] == 0) {
dist[i][j] = 0;
q.add(new int[]{i, j});
}
}
//2.一层一层往外扩
while(!q.isEmpty()) {
int[] t = q.poll();
int a = t[0], b = t[1];
for(int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if(x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {
dist[x][y] = dist[a][b] + 1;
q.add(new int[]{x, y});
}
}
}
return dist;
}
}
飞地的数量
正难则反:
从边上的 1 开始搜索,把与边上的 1 相连的联通区域全部标记⼀下;然后再遍历⼀遍矩阵,看看哪些位置的 1 没有被标记即可
标记的时候,可以用「多源 bfs 」解决。
class Solution {
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
public int numEnclaves(int[][] grid) {
int m = grid.length, n = grid[0].length;
boolean[][] vis = new boolean[m][n];
Queue<int[]> q = new LinkedList<>();
//1.把边上的1全部加到队列中
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
if(i == 0 || i == m -1 || j == 0 || j == n - 1) {
if(grid[i][j] == 1) {
q.add(new int[]{i, j});
vis[i][j] = true;
}
}
//2.多源bfs
while(!q.isEmpty()) {
int[] t = q.poll();
int a = t[0], b = t[1];
for(int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && !vis[x][y]) {
q.add(new int[]{x, y});
vis[x][y] = true;
}
}
}
//3.提取结果
int ret = 0;
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
if(grid[i][j] == 1 && !vis[i][j])
ret++;
return ret;
}
}
地图中的最高点
class Solution {
int[] dx = {0, 0, -1, 1};
int[] dy = {1, -1, 0, 0};
public int[][] highestPeak(int[][] isWater) {
int m = isWater.length, n = isWater[0].length;
int[][] dist = new int[m][n];
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
dist[i][j] = -1;
Queue<int[]> q = new LinkedList<>();
//1.所有的水域加入到队列里面
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++)
if(isWater[i][j] == 1) {
q.add(new int[]{i, j});
dist[i][j] = 0;
}
//2.多源bfs
while(!q.isEmpty()) {
int[] t = q.poll();
int a = t[0], b = t[1];
for(int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if(x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {
dist[x][y] = dist[a][b] + 1;
q.add(new int[]{x, y});
}
}
}
return dist;
}
}
地图分析
class Solution {
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
public int maxDistance(int[][] grid) {
int n = grid.length;
int[][] dist = new int[n][n];
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
dist[i][j] = -1;
Queue<int[]> q = new LinkedList<>();
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(grid[i][j] == 1) {
dist[i][j] = 0;
q.add(new int[]{i, j});
}
int ret = -1;
while(!q.isEmpty()) {
int[] t = q.poll();
int a = t[0], b = t[1];
for(int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if(x >= 0 && x < n && y >= 0 && y < n && dist[x][y] == -1) {
dist[x][y] = dist[a][b] + 1;
q.add(new int[]{x, y});
ret = Math.max(ret, dist[x][y]);
}
}
}
return ret;
}
}
拓扑排序(核心与多源BFS一样)
找到做事情的先后顺序,拓扑排序的结果可能不是唯一的
做题思路
1.把图中入度为0的点加⼊到队列中
2. 删除与该点连接的边
3. 重复1、2操作,直到图中没有点或者没有入度为0的点(有环)
拓扑排序的重要应用:判断有向图是否有环
课程表
两种建图方式
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
//1.准备工作
int[] in = new int[numCourses];//统计每一个顶点的入度
Map<Integer, List<Integer>> edges = new HashMap<>();//邻接表存图
//2.建图
for(int i = 0; i < prerequisites.length; i++) {
int a = prerequisites[i][0], b = prerequisites[i][1];
if(!edges.containsKey(b)) {
edges.put(b, new ArrayList<>());
}
edges.get(b).add(a);
in[a]++;
}
//3.拓扑排序
Queue<Integer> q = new LinkedList<>();
//(1)先把入度为0的点加入队列中
for(int i = 0; i < numCourses; i++) {
if(in[i] == 0) q.add(i);
}
//(2)bfs
while(!q.isEmpty()) {
int t = q.poll();
for(int a: edges.getOrDefault(t, new ArrayList<>())) {
if(--in[a] == 0) q.add(a);
}
}
//4.判断是否有环
for(int x: in)
if(x != 0) return false;
return true;
}
}
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> edges = new ArrayList<>();
for(int i = 0; i < numCourses; i++) {
edges.add(new ArrayList<>());
}
int[] in = new int[numCourses];
//建图
for(int i = 0; i < prerequisites.length; i++) {
int a = prerequisites[i][0], b = prerequisites[i][1];
edges.get(b).add(a);
in[a]++;
}
Queue<Integer> q = new LinkedList<>();
for(int i = 0; i < numCourses; i++) {
if(in[i] == 0) q.add(i);
}
while(!q.isEmpty()) {
int t = q.poll();
for(int x: edges.get(t)) {
if(--in[x] == 0) q.add(x);
}
}
for(int x: in)
if(x != 0) return false;
return true;
}
}
课程表II
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
//1.准备工作
List<List<Integer>> edges = new ArrayList<>();//用邻接表来存储图
for(int i = 0; i < numCourses; i++) {
edges.add(new ArrayList<>());
}
int[] in = new int[numCourses];//统计每个节点的入度
//2.建图
for(int i = 0; i < prerequisites.length; i++) {
int a = prerequisites[i][0], b = prerequisites[i][1];
edges.get(b).add(a);
in[a]++;
}
//3.拓扑排序
Queue<Integer> q = new LinkedList<>();
for(int i = 0; i < numCourses; i++) {
if(in[i] == 0) q.add(i);
}
int[] ret = new int[numCourses];
int index = 0;
while(!q.isEmpty()) {
int t = q.poll();
ret[index++] = t;
for(int a: edges.get(t))
if(--in[a] == 0)
q.add(a);
}
//4.判断
if(index == numCourses) return ret;
else return new int[0];
}
}
火星词典
class Solution {
Map<Character, Set<Character>> edges = new HashMap<>();//邻接表
Map<Character, Integer> in = new HashMap<>();//统计每个节点的入度
boolean check;
public String alienOrder(String[] words) {
//1.初始化入度哈希表 + 建图
for(String s: words) {
for(int i = 0; i < s.length(); i++) {
in.put(s.charAt(i), 0);
}
}
int n = words.length;
for(int i = 0; i < n; i++) {
for(int j = i + 1; j < n; j++) {
add(words[i], words[j]);
if(check == true) return "";
}
}
//2.拓扑排序
Queue<Character> q = new LinkedList<>();
for(char ch: in.keySet()) {
if(in.get(ch) == 0) q.add(ch);
}
StringBuilder ret = new StringBuilder();
while(!q.isEmpty()) {
char t = q.poll();
ret.append(t);
if(!edges.containsKey(t)) continue;
for(char ch: edges.get(t)) {
in.put(ch, in.get(ch) - 1);
if(in.get(ch) == 0) q.add(ch);
}
}
//3.判断
for(int a: in.values()) {
if(a != 0) return "";
}
return ret.toString();
}
void add(String s1, String s2) {
int n = Math.min(s1.length(), s2.length());
int i = 0;
for(; i < n; i++) {
char c1 = s1.charAt(i), c2 = s2.charAt(i);
if(c1 != c2) {
if(!edges.containsKey(c1)) {
edges.put(c1, new HashSet<>());
}
if(!edges.get(c1).contains(c2)) {
edges.get(c1).add(c2);
in.put(c2, in.get(c2) + 1);
}
break;
}
}
if(i == s2.length() && i < s1.length()) check = true;
}
}