leetcode
leetcode 51 ~ 100
最大子数组和

最大子数组和

难度:

标签:

题目描述

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

 

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]
输出:1

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

 

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

 

进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

代码结果

运行时间: 52 ms, 内存: 15.3 MB


/**
 * 题目思路:
 * 使用Java Stream API实现Kadane算法。我们通过IntStream和reduce操作来计算最大子数组和。
 * 初始值使用一个int数组来保存当前和和最大和,
 * 每次更新时,比较当前元素和当前和加上当前元素的大小来确定当前和。
 */
import java.util.stream.IntStream;
 
public class Solution {
    public int maxSubArray(int[] nums) {
        return IntStream.range(0, nums.length)
                .mapToObj(i -> new int[]{nums[i], nums[i]})
                .reduce(new int[]{nums[0], nums[0]}, (a, b) -> {
                    a[1] = Math.max(b[0], a[1] + b[0]);
                    a[0] = Math.max(a[0], a[1]);
                    return a;
                })[0];
    }
}

解释

方法:

这个题解使用了动态规划的思想。通过遍历数组,对于每个元素,判断将其加入当前子数组是否会使当前子数组的和增大。如果当前子数组的和小于0,那么加上当前元素会使子数组的和变小,因此将当前子数组的和重置为当前元素。否则,将当前元素加入子数组。同时,更新最大子数组和。最终返回遍历过程中得到的最大子数组和。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在动态规划的解法中,当当前子数组的和小于0时,选择将子数组的和重置为当前元素,而不是继续累加?
在动态规划中,如果当前子数组的和小于0,意味着它对增加未来元素的总和没有贡献,反而会减少总和。因此,最优的选择是放弃当前的子数组和,从新的元素重新开始计算子数组的和。这样可以确保子数组的最大可能和不会被一个负的子数组和所拖累。
🦆
在动态规划的解法中,最大子数组和`max_sum`初始化为负无穷大是出于什么考虑?是否有其他可能的初始化方式?
初始化`max_sum`为负无穷大是为了确保在遍历数组的过程中,任何一个元素的值都能被考虑进最大子数组和中,即使所有元素都是负数。这种初始化方法保证了`max_sum`能够正确地更新为数组中的最大值。另一种可能的初始化方式是将`max_sum`初始化为数组中的第一个元素,但这需要额外的逻辑来确保数组不为空。
🦆
题解中提到的动态规划解法有没有考虑到所有的边界情况,例如数组中所有元素都是负数,或者数组只包含一个元素的情况?
是的,题解中的动态规划解法已经考虑了所有边界情况。即使数组中所有元素都是负数,通过初始化`max_sum`为负无穷并在每个元素上更新它,可以保证找到最大的子数组和,即便这个和依然是负数。对于只包含一个元素的数组,算法会将这个单一元素作为子数组,因此也能得到正确结果。
🦆
动态规划的方法是否能够有效处理数组长度非常大(接近105)的情况,对于这种情况有没有性能上的考量?
动态规划的方法在处理大数组(如长度接近10^5)时仍然有效,因为它的时间复杂度是O(n),这意味着算法对每个元素只进行一次操作。尽管如此,对于非常大的数组,还需要考虑内存使用和可能的整数溢出问题。在实际应用中,可能需要额外的逻辑来处理如整数溢出等边界和异常情况。

相关问题

买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

 

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

 

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 104

乘积最大子数组

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

测试用例的答案是一个 32-位 整数。

 

示例 1:

输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

示例 2:

输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

 

提示:

  • 1 <= nums.length <= 2 * 104
  • -10 <= nums[i] <= 10
  • nums 的任何前缀或后缀的乘积都 保证 是一个 32-位 整数

数组的度

给定一个非空且只包含非负数的整数数组 nums,数组的 的定义是指数组里任一元素出现频数的最大值。

你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。

 

示例 1:

输入:nums = [1,2,2,3,1]
输出:2
解释:
输入数组的度是 2 ,因为元素 1 和 2 的出现频数最大,均为 2 。
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组 [2, 2] 的长度为 2 ,所以返回 2 。

示例 2:

输入:nums = [1,2,2,3,1,4,2]
输出:6
解释:
数组的度是 3 ,因为元素 2 重复出现 3 次。
所以 [2,2,3,1,4,2] 是最短子数组,因此返回 6 。

 

提示:

  • nums.length 在 150,000 范围内。
  • nums[i] 是一个在 049,999 范围内的整数。

最长湍流子数组

给定一个整数数组 arr ,返回 arr 的 最大湍流子数组的长度 

如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是 湍流子数组 。

更正式地来说,当 arr 的子数组 A[i], A[i+1], ..., A[j] 满足仅满足下列条件时,我们称其为湍流子数组

  • 若 i <= k < j :
    • k 为奇数时, A[k] > A[k+1],且
    • k 为偶数时,A[k] < A[k+1]
  • 若 i <= k < j :
    • k 为偶数时,A[k] > A[k+1] ,且
    • k 为奇数时, A[k] < A[k+1]

 

示例 1:

输入:arr = [9,4,2,10,7,8,8,1,9]
输出:5
解释:arr[1] > arr[2] < arr[3] > arr[4] < arr[5]

示例 2:

输入:arr = [4,8,12,16]
输出:2

示例 3:

输入:arr = [100]
输出:1

 

提示:

  • 1 <= arr.length <= 4 * 104
  • 0 <= arr[i] <= 109