leetcode
leetcode 2051 ~ 2100
删除操作后的最大子段和

删除操作后的最大子段和

难度:

标签:

题目描述

代码结果

运行时间: 296 ms, 内存: 38.1 MB


/*
 * Solution using Java Stream API
 * 
 * Idea: Similar to the above solution but using Java Streams for the operations
 * like calculating prefix sums and finding maximums. Since Streams are not optimal
 * for this type of problem, this implementation is just for demonstration.
 */

import java.util.*;
import java.util.stream.IntStream;

public class StreamSolution {
    public int[] maximumSegmentSum(int[] nums, int[] removeQueries) {
        int n = nums.length;
        int[] answer = new int[n];
        long[] prefixSum = new long[n + 1];
        IntStream.range(0, n).forEach(i -> prefixSum[i + 1] = prefixSum[i] + nums[i]);
        Set<Integer> removed = new HashSet<>();
        long maxSum = Arrays.stream(nums).sum();
        for (int i = 0; i < n; i++) {
            int removeIndex = removeQueries[i];
            removed.add(removeIndex);
            maxSum = Long.MIN_VALUE;
            int start = 0;
            while (start < n) {
                while (start < n && removed.contains(start)) start++;
                int end = start;
                while (end < n && !removed.contains(end)) end++;
                long sum = IntStream.range(start, end).mapToLong(j -> nums[j]).sum();
                maxSum = Math.max(maxSum, sum);
                start = end;
            }
            answer[i] = (int) maxSum;
        }
        return answer;
    }
}

解释

方法:

本题解采用逆向思维处理问题。初始时,所有元素都被视为删除,然后逐步添加回来,同时更新和维护子段的最大和。为了有效管理子段,使用两个数组:f记录每个子段的和,valid_range记录子段的边界。每次添加元素时,根据其左右邻居更新子段信息,合并相邻子段,并更新全局最大子段和。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在逆向添加元素的过程中,你是如何判断一个元素是否应该与其左右邻居合并成一个子段的?
在逆向添加元素的过程中,判断一个元素是否与其左右邻居合并依赖于邻居元素是否已经是某个有效子段的一部分。具体地,通过检查数组`valid_range`中对应位置是否有有效的边界值来判断。如果左邻居的右边界是有效的,即`valid_range[x-1] >= 0`,则该元素可以与左侧子段合并;同理,如果右邻居的左边界是有效的,即`valid_range[x+1] >= 0`,则可以与右侧子段合并。这样的判断确保了只有当前元素的直接邻居已被添加并形成了有效子段时,才考虑合并操作。
🦆
当合并子段时,为什么选择更新左右边界的子段和为新计算的总和,而不是只更新当前元素所在的子段?
在合并子段时,更新左右边界的子段和为新计算的总和是因为合并后的新子段实际上是一个统一的整体,跨越了原来分开的各个小子段。如果只更新当前元素所在的子段,那么在未来的查询和操作中,无法正确地反映出合并后整个子段的总和。更新左右边界确保了无论从哪个端点查找子段,都能得到正确的子段总和,这对于后续操作和最大子段和的更新至关重要。
🦆
逆向处理查询时,为何从数组的末尾向前处理,而不是从开始到末尾?
逆向处理查询,即从数组的末尾向前处理,主要是为了模拟元素逐个被添加回数组的过程。这种处理方式使问题变得更简单,因为每次添加元素时都只需要考虑当前元素与其可能的左右邻居是否形成新的子段。这样可以从一个全无元素的状态开始,逐步添加,每次操作都直接关注添加点的局部环境,避免了复杂的全局重构,从而有效维护和更新子段信息和最大子段和。
🦆
数组 `valid_range` 的具体作用是什么,它如何帮助维护和更新子段的边界信息?
数组 `valid_range` 主要用来记录每个子段的边界信息。对于每个位置i,`valid_range[i]` 表示如果i是某个子段的一部分,这个子段的另一端的边界位置。通过更新和查询这个数组,算法可以快速地确定任何元素是否属于某个有效子段,并可以立即得知该子段的整体边界。这种快速定位边界的能力是合并子段和查询子段总和时不可或缺的,它确保了每次元素添加或合并操作都能快速准确地进行,从而高效地更新和维护子段和最大子段和。

相关问题