力扣刷题4-dfs、贪心、二叉

76 阅读5分钟

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;
}