leetcode
leetcode 1701 ~ 1750
所有子数组最小值中的最大值

所有子数组最小值中的最大值

难度:

标签:

题目描述

代码结果

运行时间: 204 ms, 内存: 33.2 MB


/*
 * LeetCode Problem 1950: Maximum of Minimum Values in All Subarrays using Java Streams
 * 
 * Problem Statement:
 * Given an array of integers, find the maximum of the minimum values in all subarrays of the array.
 * 
 * Approach:
 * 1. Use a monotonic stack to find the previous and next less elements for each element in the array.
 * 2. Calculate the contribution of each element as the minimum in its subarrays.
 * 3. Aggregate the results to find the maximum of these minimum values.
 */

import java.util.Arrays;
import java.util.Stack;

public class Solution {
    public int maxOfMinValues(int[] arr) {
        int n = arr.length;
        int[] previousLess = new int[n];
        int[] nextLess = new int[n];
        Stack<Integer> stack = new Stack<>();

        // Find previous less for each element
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
                stack.pop();
            }
            previousLess[i] = (stack.isEmpty()) ? -1 : stack.peek();
            stack.push(i);
        }

        stack.clear();

        // Find next less for each element
        for (int i = n - 1; i >= 0; i--) {
            while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
                stack.pop();
            }
            nextLess[i] = (stack.isEmpty()) ? n : stack.peek();
            stack.push(i);
        }

        // Calculate maximum of minimum values in all subarrays
        int[] result = new int[n + 1];
        Arrays.fill(result, Integer.MIN_VALUE);
        for (int i = 0; i < n; i++) {
            int length = nextLess[i] - previousLess[i] - 1;
            result[length] = Math.max(result[length], arr[i]);
        }

        // Fill in the rest of the results array
        for (int i = n - 1; i >= 1; i--) {
            result[i] = Math.max(result[i], result[i + 1]);
        }

        return result[1]; // result[1] holds the final answer
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[] arr = {1, 2, 3, 4, 5};
        System.out.println(sol.maxOfMinValues(arr)); // Expected output: 1
    }
}

解释

方法:

此题解采用单调栈和动态规划的思想来寻找所有子数组的最小值中的最大值。首先,使用单调栈来确定每个元素作为最小值可以扩展到的最左和最右边界。数组left和right分别存储每个元素作为最小值时,左边和右边的边界索引。接着,利用这些边界信息,计算每个可能的子数组长度(从1到n)中的最小值的最大值,并存储在数组ans中。最后,从后向前遍历数组ans,确保每一个长度的最小值的最大值是正确的(即长度为k的所有子数组的最小值的最大值,不会小于长度为k+1的对应值)。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在单调栈的使用中,为什么选择使用单调递增栈而不是单调递减栈来确定元素的左右边界?
使用单调递增栈的目的是为了在数组中维护一个非递增的元素序列。当遇到一个新元素比栈顶元素小,即打破了递增趋势时,栈顶元素的右边界自然就确定为当前元素的索引。这样可以确保每个元素作为最小值时,其能扩展的最大范围被正确记录。如果使用单调递减栈,栈中的元素将按递减顺序排列,这样无法直接找到作为最小值时的最大右边界,因此不适用于本问题。
🦆
你是如何确保每个元素作为最小值时,其左右边界值left和right的正确性的?有没有可能出现漏判或者重复判定的情况?
在算法中,通过逐个检查数组元素并更新栈来确保边界的正确性。对于每个元素,当它导致栈顶元素出栈时,就会更新那些元素的右边界为当前元素的索引。同时,当前元素的左边界则是栈中前一个元素的索引。这种方式确保了每个元素作为最小值时的左右边界只会被设定一次,从而避免了漏判或重复判定的情况。
🦆
为什么在更新ans数组时,使用的索引是`r-l-2`?这个计算方式背后的逻辑是什么?
在更新ans数组时使用的索引`r-l-2`是基于左右边界的定义计算出子数组的长度。由于左右边界是不包含在子数组中的,所以实际子数组的长度为`r - l - 1`。但由于数组索引是从0开始的,对应的长度为`k`的子数组在ans中的索引应为`k-1`,因此使用`r-l-2`来更新ans数组。这样的计算确保每个子数组长度的最小值能被正确地归类和更新。
🦆
在反向遍历ans数组时,你提到要`确保每一个长度的最小值的最大值是正确的`,这个操作的目的是什么?为什么单纯的正向填充ans数组不能保证值的正确性?
反向遍历ans数组并更新其值的目的是确保较短长度的子数组的最小值不会被较长长度的子数组的最小值所覆盖。因为在正向填充ans数组时,可能仅考虑了局部最优解,没有考虑全局最优解,即可能存在某些长子数组的最小值实际上小于某些短子数组的最小值。因此,通过反向遍历并更新,我们确保每个长度为k的子数组的最小值至少不小于长度为k+1的子数组的最小值,从而维持整体的一致性和正确性。

相关问题