力扣数组练习题(缺失的第一个正数、接雨水)

35 阅读3分钟

缺失的第一个正数

来源:力扣(LeetCode) 链接:leetcode.cn/problems/fi…

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

输入:nums = [1,2,0]

输出:3

示例 2:

输入:nums = [3,4,-1,1]

输出:2

示例 3:

输入:nums = [7,8,9,11,12]

输出:1

提示:

  • 1 <= nums.length <= 5 * 105
  • -231 <= nums[i] <= 231 - 1

代码

class Solution {
    public int firstMissingPositive(int[] nums) {
        int n = nums.length;
        
        // 步骤1:将所有非正数替换为n+1
        for (int i = 0; i < n; i++) {
            if (nums[i] <= 0) {
                nums[i] = n + 1;
            }
        }
        
        // 步骤2:将正数对应的索引位置标记为负数
        for (int i = 0; i < n; i++) {
            int num = Math.abs(nums[i]);
            if (num <= n) {
                nums[num - 1] = -Math.abs(nums[num - 1]);
            }
        }
        
        // 步骤3:找到第一个未被标记的索引位置
        for (int i = 0; i < n; i++) {
            if (nums[i] > 0) {
                return i + 1;
            }
        }
        
        // 如果所有索引位置都被标记,则返回n+1
        return n + 1;
    }
}

思路分析

  1. 首先,我们遍历数组,将所有非正数(小于等于0)替换为n+1。这是因为我们只关心数组中的正数,并且缺失的最小正整数一定在1到n+1之间,所以将非正数替换为n+1可以使得这些非正数不影响我们的计算。
  2. 接下来,我们再次遍历数组。对于每个正数,我们将其对应的索引位置的数值标记为负数。标记的方式是取绝对值并加上负号。这一步的目的是用正负号标记数组中出现过的正数。
  3. 最后,我们再次遍历数组。找到第一个未被标记为负数的索引位置,该位置对应的数值加1即为缺失的最小正整数。如果所有索引位置都被标记为负数,则说明数组中的所有正整数都出现过,缺失的最小正整数为n+1。

接雨水

来源:力扣(LeetCode) 链接:leetcode.cn/problems/tr…

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

图片.png

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]

输出:6

解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]

输出:9

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105

代码

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        if (n == 0) return 0;
        
        int[] leftMax = new int[n]; // 记录每个位置左侧的最大高度
        int[] rightMax = new int[n]; // 记录每个位置右侧的最大高度
        
        // 遍历数组,计算每个位置左侧的最大高度
        leftMax[0] = height[0];
        for (int i = 1; i < n; i++) {
            leftMax[i] = Math.max(leftMax[i - 1], height[i]);
        }
        
        // 遍历数组,计算每个位置右侧的最大高度
        rightMax[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            rightMax[i] = Math.max(rightMax[i + 1], height[i]);
        }
        
        int ans = 0;
        // 遍历数组,计算每个位置可以存储的雨水量
        for (int i = 0; i < n; i++) {
            int minHeight = Math.min(leftMax[i], rightMax[i]); // 左右两侧较小的高度
            ans += minHeight - height[i];
        }
        
        return ans;
    }
}

思路分析

  1. 首先,我们创建两个数组leftMaxrightMax,分别用于记录每个位置左侧和右侧的最大高度。
  2. 遍历数组,计算每个位置左侧的最大高度。从左往右遍历,当前位置的最大高度为前一个位置的最大高度与当前位置高度的较大值。
  3. 遍历数组,计算每个位置右侧的最大高度。从右往左遍历,当前位置的最大高度为后一个位置的最大高度与当前位置高度的较大值。
  4. 再次遍历数组,计算每个位置可以存储的雨水量。对于每个位置,找到其左侧和右侧较小的最大高度,然后用该最小高度减去当前位置的高度,即为该位置可以存储的雨水量。累加所有位置的雨水量即为最终的答案。