Maximum Subarray

Maximum Subarray

description:
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example:

Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Follow up:

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

这道题给出一个整数数组,让我们求出连续子数组中和最大的一个,这是一道经典的历史名题了。题目让我们试着用O(n)和分治的方法来做。

下面我来介绍此题的三种解法:

  • solution1:

O(n)解法:O(n)线性时间复杂度的算法最先开始是在1977年被卡内基梅隆大学的教授Kadane发现并提出来的,因此也被称为Kadane’s algorithm。算法思想是:定义一个cur记录子数组的和,遍历数组,对于每一个数,去判断(之前子数组的和cur + 这个数num)和(这个数num)的大小,并让cur取它们中的较大者。也就是说只要前面的cur>0,就能使得子数组的和继续增大,cur=cur+num。否则cur=num,相当于前面的子数组和为负数,就重新选定num为新一轮cur的子数组元素,然后继续遍历。还要定义一个res,用来记录每次cur的最大值,这样把每次cur得到的结果取了一个最大值,也就是最终的结果了。

Mycode1:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        // Input: -2 1 -3 4 -1 2 1 -5 4
        // Output: 6
        //curSum记录当前前缀和的值, res记录每次前缀和的一个最大值
        int res = INT_MIN, curSum = 0;
        for(int num : nums){
            //把当前前缀和和后一位的数字比较,如果当前前缀和<0(产生负影响),则会取num值
            curSum = max(curSum + num, num);
            //cout << curSum << ' ';
            res = max(res, curSum);
        }
        return res;
    }
};

  • solution2: O(nlogn)

分治思想:有点类似二分,把一个大规模的要处理的问题划分成小的模块一个个去解决。算法思想:把题目所给的数组划分成两个数组,分别递归地去求左侧数组和右侧数组的最大子数组和。这样考虑出来的是左侧数组和右侧数组的maximum subarray,整个数组的最大子数组和还有可能经过中间的数,因此还有求一个由中间数出发的一个最大子数组和,然后把这三个最大值取一个max,就是答案了!

Mycode2:

class Solution {
public:
    int find(vector<int>& nums, int left, int right){
        //boundary
        if (left == right) return nums[left];
        if (left > right) return INT_MIN;
        //declare
        int mid = (left + right) / 2;
        int lmax = 0, rmax = 0, ml = nums[mid], mr = 0;
        //find
        lmax = find(nums, left, mid - 1);
        rmax = find(nums, mid + 1, right);
        //find middle
            //1. left
        for(int i = mid, sum = 0; i >= left; i--){
            sum += nums[i];
            ml = max(ml, sum);
        }
            //2. right
        for(int i = mid + 1, sum = 0; i <= right; i++){
            sum += nums[i];
            mr = max(mr, sum);
        }
        return max(max(lmax, rmax), ml + mr);
    }

    int maxSubArray(vector<int>& nums){
        return find(nums, 0, nums.size() - 1);
    }
};

  • solution3:

也是O(n)的解法,和solution1有点相似,动态规划思想,但写法非常简单。算法思想是:把num数组打造成一个记录前缀子数组最大和的数组,nums[i]表示由0 ~ i的子数组最大和。于是nums[0]只能等于nums[0],循环由1 ~ len(nums)-1,如果前一个子数组最大和nums[i-1]<0,则能使的后面子数组和变大,于是nums[i] += nums[i-1],如果nums[i-1]<0,则只能使后面变小,则num[i]不变。最后在整个前缀子数组最大和的值里面取一个max就是答案了!

Mycode3:

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        for i in range(1, len(nums)):
            if (nums[i - 1] > 0):
                nums[i] += nums[i - 1]
        return max(nums)        

O(n)的解法看着简单,理解起来不容易,要在草稿纸上多推算加深理解。
分治的解法博大精深,这种思想应用在很多经典算法里面,如快速排序,归并排序等等。算法之路道阻且长,吾将上下而求索!

谢谢你的观看!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值