Day41LC+好未来

1. 最多能完成排序的块 II

这个问题和“最多能完成排序的块”相似,但给定数组中的元素可以重复,输入数组最大长度为2000,其中的元素最大为10**8。
arr是一个可能包含重复元素的整数数组,我们将这个数组分割成几个“块”,并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。我们最多能将数组分成多少块?

分析:

排序块的充分条件是:此块中最大数字为 headhead , 若此块后面的所有数字都 >= head ,则此块为排序块。

方法一:辅助栈

可以遍历一遍数组arr,动态判断到目前数字 num 为止最多能分出多少排序块,并保存每个排序块的最大值 head 。每遍历到下个数字 num,动态判断前面所有的排序块是否成立,并更新所有排序块:

  • 当某排序块 num < head :将此排序块[A]与 num 合并,形成新排序块[A | num],最大值仍为 head;
  • 当某排序快num>=head:原排序块保留,并新加排序块 [num]。
    对于整个数组的排序块,其 head大小是从左到右递增的。因此,若给数组尾部加入一个随机正整数 n ,尾部的排序块更容易被合并(最先满足 num < head )。当 n 值较小时( < 前面多个排序块的 head ),则需按尾部到首部的顺序合并多个排序块。使用 栈 保存排序块最大值 head 。在遍历过程中,通过维护栈的 head 序列,实现排序块的动态更新。
class Solution {
    public int maxChunksToSorted(int[] arr) {
        LinkedList<Integer> stack = new LinkedList<Integer>();
        for(int num : arr) {
            if(!stack.isEmpty() && num < stack.getLast()) {
                int head = stack.removeLast();
                while(!stack.isEmpty() && num < stack.getLast()) stack.removeLast();
                stack.addLast(head);
            }
            else stack.addLast(num);
        }
        return stack.size();
    }
}

贪心+前缀最大值+后缀最小值
  1. 如果前缀 [… i) 的最大值 <= 后缀 [i …] 的最小值,则意味着 i 及其右侧的元素都能“守得住”,即:原数组排序后,[i] 无需跨到左侧,此时增加一个分块,对排序结果无影响;
  2. 否则,意味着 [i] 位置“守不住”,在原数组排序后,需要将其移到左侧,因此它不能单独成一个分块,此时分块不能增加;
class Solution {
    public int maxChunksToSorted(int[] arr) {
        if (arr.length == 0) return 0;
        int[] rightMin = new int[arr.length];
        int leftMax = arr[0];
        rightMin[arr.length-1] = arr[arr.length-1];
        for (int i = arr.length-2; i >= 0 ; i--) {
            rightMin[i] = Math.min(arr[i], rightMin[i+1]);
        }
        int res = 1;
        for (int i = 1; i < arr.length; i++) {
            if (leftMax <= rightMin[i]) res++;
            leftMax = Math.max(leftMax, arr[i]);
        }
        return res;
    }
}

2. 岛屿数量

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

分析:

DFS。扫描整个二维网格。如果一个位置为 1,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。最终岛屿的数量就是我们进行深度优先搜索的次数。

class Solution {
    public int numIslands(char[][] grid) {
        if (grid.length == 0 || grid[0].length == 0) return 0;
        int lines = grid.length;
        int rows = grid[0].length;
        int ans = 0;
        for (int i = 0; i < lines; i++) {
            for (int j = 0; j < rows; j++) {
                if (grid[i][j] == '1')
                {
                    traceback(grid, i, j);
                    ans++;
                }
            }
        }
        return ans;
    }

    private void traceback(char[][] grid, int i, int j) {
        if (i<0 || i>=grid.length || j<0 || j>=grid[0].length || grid[i][j] == '0') return;
        grid[i][j] = '0';
        traceback(grid, i-1, j);
        traceback(grid, i+1, j);
        traceback(grid, i, j-1);
        traceback(grid, i, j+1);
    }
}

3. 二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

分析:

DFS。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        PreOrder(root, res);
        return res;
    }

    private void PreOrder(TreeNode root, List<Integer> res) {
        if (root == null) return;
        res.add(root.val);
        PreOrder(root.left, res);
        PreOrder(root.right, res);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值