1 DFS算法
二维矩阵中使用DFS搜索
// 二维矩阵dfs遍历
void dfs(int[][] grid, int i, int j, boolean[][] visited) {
int m = grid.length;
int n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
return;
}
if (visited[i][j]) {
return;
}
// 前序,进入节点[i,j]
visited[i][j] = true;
dfs(grid, i - 1, j, visited);
dfs(grid, i, j - 1, visited);
dfs(grid, i + 1, j, visited);
dfs(grid, i, j + 1, visited);
// 后序访问,离开[i,j]
visited[i][j] = true;
}
200. 岛屿数量
题目描述:给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
思路: FloodFill算法。循环遍历网格,每遇到陆地,岛屿数+1,把与之相邻的陆地dfs变成海水。为什么淹没陆地,原因:省事,不用visited数组。
public int numIslands(char[][] grid) {
int m = grid.length;
int n = grid[0].length;
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1') {
res++;
dfs(grid, i, j);
}
}
}
return res;
}
private void dfs(char[][] grid, int i, int j) {
int m = grid.length;
int n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
return;
}
if (grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
dfs(grid, i, j - 1);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i + 1, j);
}
1254.封闭岛屿数量
题目描述:封闭岛是一个 完全 由1包围(左、上、右、下)的岛。请返回 封闭岛屿 的数目。
思路:
把靠边的岛屿排除掉,剩下就是封闭岛屿
public int closedIsland(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int res = 0;
for (int j = 0; j < n; j++) {
dfs(grid, 0, j); //边界岛屿 i=0
dfs(grid, m - 1, j); //边界岛屿 i=m-1
}
for (int i = 0; i < m; i++) {
dfs(grid, i, 0); //边界岛屿 j=0
dfs(grid, i, n - 1); //边界岛屿 j=n-1
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0) {
res++;
dfs(grid, i, j);
}
}
}
return res;
}
695.岛屿的最大面积
题目描述:给你一个大小为 m x n 的二进制矩阵 grid 思路: 计算最大岛屿面积,dfs淹没岛屿时记录岛屿面积
// 淹没[i,j]相邻陆地,并返回淹没陆地面积
int dfs(int[][] grid, int i, int j) {
int m = grid.length;
int n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
return 0;
}
// 已经是海水,直接返回(等价visited)
if (grid[i][j] == 0) {
return 0;
}
grid[i][j] = 0;
return dfs(grid, i - 1, j)
+ dfs(grid, i, j - 1)
+ dfs(grid, i + 1, j)
+ dfs(grid, i, j + 1) + 1;
}
463.岛屿的周长
题目描述:格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。 思路: 遍历陆地,每个陆地4个方向,只有相邻块是水或边界时,周长贡献+1。
int isPerimeter(vector<vector<int>> &grid, int i, int j, int k) {
int m = grid.size();
int n = grid[0].size();
switch (k) {
case 0: {
j--;
break;
}
case 1: {
i--;
break;
}
case 2: {
j++;
break;
}
case 3: {
i++;
break;
}
}
// boundary
if (i < 0 || j < 0 || i >= m || j >= n) {
return 1;
}
return (grid[i][j] == 0);
}
int islandPerimeter(vector<vector<int>> &grid) {
int m = grid.size();
int n = grid[0].size();
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0) {
continue;
}
for (int k = 0; k < 4; k++) {
res += isPerimeter(grid, i, j, k);
}
}
}
return res;
}
2 贪心算法
134. 加油站
题目:一条环路上有 n 个加油站,从其中的一个加油站出发,开始时油箱为空。如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
思路:
抽象gas[i]-cost[i] 为一个节点,表示经过i节点时油量变化。题目即为求累加和sum一直大于0。手动画图,可以发现从i=0开始,sum变化图,取sum最低点的Next作为start。
注意:return start == n ? 0 : start 当start值为n时,需要取模
// 抽象,gas[i]-cost[i],问题转换为:寻找一个start,使得所有sum大于等于0
// 图像解法,寻找最低点的 下一个点
public int canCompleteCircuit(int[] gas, int[] cost) {
int n = gas.length;
int sum = 0;
int minSum = 0;
int start = 0;
for (int i = 0; i < n; i++) {
sum += gas[i] - cost[i];
if (sum < minSum) {
minSum = sum;
start = i + 1;
}
}
if (sum < 0) {
return -1;
}
return start == n ? 0 : start;
}
3 二叉搜索树(BST)
BST树:左子树都比node值小,右子树都比node值大 重要性质:BST中序遍历结果是升序
void travers(TreeNode root) {
if (root == null) {
return;
}
travers(root.left);
print(root.val);
travers(root.right);
}
230.二叉搜索树中第K小元素
思路:BST升序排序,第k个元素
// 定义全局变量
int rank = 0;
int res = 0;
private void traverse(TreeNode root, int k) {
if (root == null) {
return;
}
traverse(root.left, k);
// 遍历root 节点
rank++;
if (rank == k) {
res = root.val;
return;
}
traverse(root.right, k);
}
4 二叉树
二叉树遍历框架
void travers(TreeNode root) {
//前序
print(root.val)
travers(root.left);
//中序
print(root.val)
travers(root.right);
//后序
print(root.val)
}
226. 翻转二叉树
题目描述:一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点
思路: 前序遍历二叉树,对每个二叉树翻转左右子节点
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.right = left;
root.left = right;
return root;
}
116. 填充二叉树节点右指针
题目描述:一棵二完美二叉树 ,填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL
思路: 先序遍历,每次递归需要传递两个node
/**
* 思路:先序遍历,演变 每次传递两个node
*/
public Node connect(Node root) {
if (root == null) {
return null;
}
connectTwo(root.left, root.right);
return root;
}
private void connectTwo(Node node1, Node node2) {
if (node1 == null || node2 == null) {
return;
}
node1.next = node2;
connectTwo(node1.left, node1.right);
connectTwo(node1.right, node2.left);
connectTwo(node2.left, node2.right);
}
5 接雨水问题
题目描述:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算能接多少雨水
思路:
位置i,接的水:
w[i] = min(max(h[0..i]), max(i..end)) - height[i]
备忘录解法,定义l_max r_max,保存节点i的左右最大值
/**
* 接雨水问题
* water[i] = Min(Max(height[0...i], Max(height[i+1...n-1]))) - height[i]
* 备忘录解法
*/
public int trap(int[] height) {
int n = height.length;
int water = 0;
int[] l_max = new int[n];
int[] r_max = new int[n];
l_max[0] = height[0];
r_max[n - 1] = height[n - 1];
for (int i = 1; i < n; i++) {
l_max[i] = Math.max(l_max[i - 1], height[i]);
}
for (int i = n - 2; i >= 0; i--) { // 从右->左
r_max[i] = Math.max(r_max[i + 1], height[i]);
}
for (int i = 0; i < n; i++) {
water += Math.min(l_max[i], r_max[i]) - height[i];
}
return water;
}