首页 > 其他分享 >【练习】力扣 热题100 最大子数组和

【练习】力扣 热题100 最大子数组和

时间:2025-01-12 11:00:20浏览次数:3  
标签:nums int 复杂度 maxSum 力扣 数组 sum 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

来源:力扣 热题100 最大子数组和
——————————————————————

最大子数组和问题(Maximum Subarray Problem)是一个经典的算法问题,目标是在一个整数数组中找到具有最大和的连续子数组。以下是5种解法及其详细解释:


1. 暴力枚举法(数据量很大时,此题会超时)

思路
  • 枚举所有可能的子数组,计算每个子数组的和,并记录最大值。
  • 使用双重循环,外层循环确定子数组的起点,内层循环确定子数组的终点。
代码实现
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxSum = INT_MIN; //int最小值
        for (int i = 0; i < nums.size(); i++) {
            int sum = 0;
            for (int j = i; j < nums.size(); j++) {
                sum += nums[j];
                maxSum = max(maxSum, sum);
            }
        }
        return maxSum;
    }
};
时间复杂度
  • O(N^2),其中 N 是数组的长度。
  • 外层循环运行 N 次,内层循环平均运行 N/2 次。
空间复杂度
  • O(1),只使用了常数级别的额外空间。

2. 动态规划(Kadane 算法)(最优解)

思路
  1. 使用动态规划的思想,原地修改数组 nums,将 nums[i] 更新为以 nums[i] 结尾的最大子数组和。
  2. 如果 nums[i - 1] 大于 0,说明以 nums[i - 1] 结尾的子数组和对 nums[i] 有贡献,因此将其加到 nums[i] 上。
  3. 在遍历过程中,记录 nums[i] 的最大值,即全局最大子数组和。
代码实现
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxSum = nums[0]; // 全局最大子数组和

        // 动态规划:原地修改 nums[i] 为以 nums[i] 结尾的最大子数组和
        for (int i = 1; i < nums.size(); i++) {
            // 如果 nums[i - 1] 大于 0,则将其加到 nums[i] 上
            if (nums[i - 1] > 0) {
                nums[i] += nums[i - 1];
            }
            // 更新全局最大子数组和
            maxSum = max(maxSum, nums[i]);
        }

        return maxSum;
    }
};
时间复杂度
  • O(N),其中 N 是数组的长度。
  • 只需遍历数组一次。
空间复杂度
  • O(1),只使用了常数级别的额外空间。

3. 分治法(拓展)

思路
  • 将数组分成左右两部分,分别递归求解左右部分的最大子数组和。
  • 最大子数组和可能出现在左半部分、右半部分,或者跨越左右两部分。
  • 计算跨越左右两部分的最大子数组和时,需要从中间向左右扩展。
代码实现
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        return divideAndConquer(nums, 0, nums.size() - 1);
    }

    int divideAndConquer(vector<int>& nums, int left, int right) {
        if (left == right) {
            return nums[left];
        }

        int mid = left + (right - left) / 2;

        // 分别计算左半部分和右半部分的最大子数组和
        int leftMax = divideAndConquer(nums, left, mid);
        int rightMax = divideAndConquer(nums, mid + 1, right);

        // 计算跨越左右两部分的最大子数组和
        int crossMax = maxCrossingSum(nums, left, mid, right);

        // 返回三者中的最大值
        return max({leftMax, rightMax, crossMax});
    }

    int maxCrossingSum(vector<int>& nums, int left, int mid, int right) {
        int leftSum = INT_MIN, rightSum = INT_MIN;
        int sum = 0;

        // 从中间向左扩展,计算左半部分的最大和
        for (int i = mid; i >= left; i--) {
            sum += nums[i];
            leftSum = max(leftSum, sum);
        }

        sum = 0;
        // 从中间向右扩展,计算右半部分的最大和
        for (int i = mid + 1; i <= right; i++) {
            sum += nums[i];
            rightSum = max(rightSum, sum);
        }

        // 返回跨越左右两部分的最大和
        return leftSum + rightSum;
    }
};
时间复杂度
  • O(N log N),其中 N 是数组的长度。
  • 分治法的时间复杂度由递归深度和每层的工作量决定。
空间复杂度
  • O(log N),递归调用栈的深度为 log N

4. 前缀和优化(拓展)

思路
  • 计算数组的前缀和数组 prefixSum,其中 prefixSum[i] 表示从 nums[0]nums[i] 的和。
  • 最大子数组和可以表示为 prefixSum[j] - prefixSum[i] 的最大值,其中 i < j
  • 在遍历过程中,维护 minPrefixSum,表示当前最小的前缀和。
代码实现
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxSum = nums[0];
        int minPrefixSum = 0;
        int prefixSum = 0;

        for (int num : nums) {
            prefixSum += num; // 计算当前前缀和
            maxSum = max(maxSum, prefixSum - minPrefixSum); // 更新最大子数组和
            minPrefixSum = min(minPrefixSum, prefixSum); // 更新最小前缀和
        }

        return maxSum;
    }
};
时间复杂度
  • O(N),其中 N 是数组的长度。
  • 只需遍历数组一次。
空间复杂度
  • O(1),只使用了常数级别的额外空间。

5. 贪心算法(拓展)

思路
  • 遍历数组,维护当前子数组的和 currentSum
  • 如果 currentSum 小于 0,则重置为当前元素;否则,累加当前元素。
  • 在遍历过程中,记录 currentSum 的最大值。
代码实现
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxSum = nums[0];
        int currentSum = nums[0];

        for (int i = 1; i < nums.size(); i++) {
            // 如果当前子数组和小于 0,则重置为当前元素
            if (currentSum < 0) {
                currentSum = nums[i];
            } else {
                // 否则,累加当前元素
                currentSum += nums[i];
            }

            // 更新最大子数组和
            maxSum = max(maxSum, currentSum);
        }

        return maxSum;
    }
};
时间复杂度
  • O(N),其中 N 是数组的长度。
  • 只需遍历数组一次。
空间复杂度
  • O(1),只使用了常数级别的额外空间。

6. 总结

方法时间复杂度空间复杂度特点
暴力枚举法O(N^2)O(1)简单直观,但效率低
动态规划O(N)O(1)高效,经典解法
分治法O(N log N)O(log N)分而治之,适合大规模数据
前缀和优化O(N)O(1)基于前缀和,思路清晰
贪心算法O(N)O(1)高效,与动态规划类似
  • 推荐解法:动态规划(Kadane 算法),时间复杂度为 O(N),空间复杂度为 O(1),是最优解法。

标签:nums,int,复杂度,maxSum,力扣,数组,sum,100,热题
From: https://blog.csdn.net/2402_86344613/article/details/145088153

相关文章

  • 100_基于springboot的图书管理系统
    ......
  • LeetCode热题100(二十六) —— 142.环形链表II
    LeetCode热题100(二十六)——142.环形链表II题目描述代码实现思路解析你好,我是杨十一,一名热爱健身的程序员在Coding的征程中,不断探索与成长LeetCode热题100——刷题记录(不定期更新)此系列文章用于记录我在学习LeetCode热题100过程中的总结和收获愿与诸君共同探讨,在......
  • cpu占用率为什么有时会超过100%?
    文章目录cpu占用率为什么有时会超过100%?1.多核处理器的影响2.监控工具的计算方式3.虚拟化环境的影响4.短时间峰值5.软件或驱动程序的问题结论cpu占用率为什么有时会超过100%?CPU占用率超过100%的现象在多核处理器系统中是比较常见的,主要原因如下:1.多核处......
  • 【Leetcode 热题 100】739. 每日温度
    问题背景给定一个整数数组tempera......
  • Sigrity System SI SerialLink模式进行100base_T1协议仿真分析操作指导-100BaseT1_Rx
    SigritySystemSISerialLink模式进行100base_T1协议仿真分析操作指导-100BaseT1_RxSigritySystemSISerialLink模式提供了10个协议合规性检查工具模板,用户可以将根据实际应用替换模板中的SPICE文件,然后进行协议仿真分析,同时软件还提供了目标结果的模板MASK以及该协议需要......
  • 梦开始的地方:力扣热题100哈希表
    文章目录前言一、哈希表是什么二、力扣解题常见的三种哈希结构(java版本)1.数组2.set(集合)3.map(映射)总结前言在刷力扣100题的征程中,我从哈希相关题目入手,一路探索,收获颇丰。如今,想将自己在这一过程中的思路与感悟进行一番总结,既为记录成长,也希望能给同样在算法之路上......
  • 力扣21、合并两个有序链表
    目录1、题目2、思路3、代码若有错误,欢迎指正! 1、题目将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例1:输入:l1=[1,2,4],l2=[1,3,4]输出:[1,1,2,3,4,4]示例2:输入:l1=[],l2=[]输出:[]示例......
  • 力扣283题——移除0
    要点就是不说废话,看题: 这题就是快慢指针法的进阶一点点,需要把第一次遍历完的数组再继续填空,把后面的空填充为0,这里给出我的做法:classMain{publicvoidmove(int[]nums){intn=nums.length;intslow=0;for(intfast=0;fast<n;fast++){......
  • 在 Safari 浏览器中,快速将页面恢复到 100% 缩放(也就是默认尺寸)Command (⌘) + 0 (零)
    在Safari浏览器中,没有一个专门的快捷键可以将页面恢复到默认的缩放比例。但是,你可以使用以下两种方法快速将页面恢复到100%缩放(也就是默认尺寸):方法一:使用快捷键(最常用)Command(⌘)+0(零)这个快捷键会立即将当前页面的缩放比例重置为100%。这是最常用的方式,......
  • 炸弹 (boom.c)(100分双端递推+分割线优化)
    炸弹(boom.c)时间限制:800ms内存限制:256000KiB进度:57/12406=0.5%题目描述出题助教:Sakiyary验题助教:Corax、XiEn、ErinwithBMQ、runz、MacGuffin、Bob维多利亚的腐烂荒野上出现了 N 个魔物,你和小维需要抓紧时间调配炸弹对付它们。荒野可以视为一张方格图,(x_i......