LeetCode -- 53. Maximum Subarray(最大子数组)

文章目录

  • 1. 原文
  • 2. 翻译
  • 3. 分析
    • 3.1. 普通法
    • 3.2. 动态规划法(DP)
    • 3.3. 分治递归(divide and conquer approach)
  • 4. AC代码
    • 4.1. 普通法
    • 4.2. DP
    • 4.3. 分治递归


1. 原文

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.


2. 翻译

给定一个整数数组nums,找到具有最大总和的连续子数组(至少包含一个数),并返回该最大总和。

例子:

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

看一下下面的内容:
如果你已经找到一个O(n)的解法,尝试使用分治法去编写另一种解法,这种解法更加微妙。


3. 分析

这题有三种解法,以下依次分析这三种解法。


3.1. 普通法

该方法就是一次遍历,思路很简单,如下:
首先声明一个整数变量 max_sum,初始化为数组的第一个数,该变量就是最终结果。之后就是从下标为1的位置开始遍历(因为下标为0的无须处理),每次遍历,将前面计算的最大和和该位置上的数字相加得到新的最大和,再将这个新值和旧值比较,将大值赋值给 max_sum,再判断一下 temp_sum 是否小于0,如果小于0则可以直接舍弃(此处的舍弃就是再赋值为0),因为小于0的数加上一个数只会更小。


3.2. 动态规划法(DP)

DP就很简单了,但是DP的重难点就是在找到状态转移方程。(此处不详细介绍DP,大家可以移步谷歌搜索DP,会有大量的dalao讲解DP)。

既然重难点是状态转移方程,那么该题中的状态转移方程是什么呢?

d p ( i ) = { n u m s ( 0 ) , i = 0 m a x { d p ( i − 1 ) + n u m s ( i ) , n u m s ( i ) } , i > 0 dp(i) = \left\{ \begin{aligned} nums(0) & , & i=0 \\ max\lbrace{dp(i-1)+nums(i), nums(i)\rbrace} & , & i>0 \\ \end{aligned} \right. dp(i)={nums(0)max{dp(i1)+nums(i),nums(i)},,i=0i>0

找到状态转移方程的话那代码就好写了,代码看下面,其实很好理解的。

首先,将最大和初始化为第一个数(当长度为1时也是正确的),之后遍历数组,将前面的最大和和当前值相加,再将结果与当前值进行比较取最大值更新 dp 数组(此处就是状态转移方程中的第二个式子)。

为什么此处就可以更新呢?

仔细想想,当前值加上之前的最大和的和却比当前值小,那可以肯定的就是之前的最大和是一个负数,只有是负数才会使其和变小,所以就可以将其更新为当前值。


3.3. 分治递归(divide and conquer approach)

分治就比较好理解,但是其实运行时间明显大于前两种方法(在LeetCode上提交我的运行时间内是40ms,是前两种方法的5倍)。

找到中间位置,其结果只有三种可能:一是在左边,二是在右边,三是在中间位置两边。所以我们就先递归左边找到左边的最大值(即第一种可能结果),再递归右边找到右边的最大值(即第二种可能结果),之后再从左边的尾部和右边的首部开始遍历,找出中间位置两边的最大值,其和就是第三种可能结果,最后就是比较这三个值了,返回最大的。


4. AC代码

4.1. 普通法

int maxSubArray(vector<int>& nums) {
    int max_sum = nums[0];
    int temp_sum = max_sum > 0 ? max_sum : 0;
    for (int i=1; i<nums.size(); ++i) {
        temp_sum += nums[i];
        max_sum = temp_sum > max_sum ? temp_sum : max_sum;
        temp_sum = temp_sum > 0 ? temp_sum : 0;
    }
    return max_sum;
}

4.2. DP

int maxSubArray(vector<int>& nums) {
    int dp[nums.size()];
    dp[0] = nums[0];
    int ans = dp[0];
    for (int i=1; i<nums.size(); ++i) {
        dp[i] = max(dp[i-1]+nums[i], nums[i]);
        ans = max(ans, dp[i]);
    }
    return ans;
}

4.3. 分治递归

int maxSubArray(vector<int>& nums) {
    if (nums.size() == 1) {
        return nums[0];
    }
    vector<int> left_vector(nums.begin(), nums.begin() + nums.size() / 2);
    vector<int> right_vector(nums.begin() + nums.size() / 2, nums.end());
    int left_max1 = maxSubArray3(left_vector);
    int right_max1 = maxSubArray3(right_vector);

    int left_max2 = left_vector[left_vector.size() - 1];
    int left_max2_temp = left_max2;
    for (int i=left_vector.size()-2; i>=0; --i) {
        left_max2_temp += left_vector[i];
        left_max2 = max(left_max2, left_max2_temp);
    }

    int right_max2 = right_vector[0];
    int right_max2_temp = right_max2;
    for (int i=1; i<right_vector.size();++i) {
        right_max2_temp += right_vector[i];
        right_max2 = max(right_max2, right_max2_temp);
    }

    if (left_max1 >= right_max1 && left_max1 >= (left_max2+right_max2)) {
        return left_max1;
    }
    if (left_max1 <= right_max1 && right_max1 >= (left_max2+right_max2)) {
        return right_max1;
    }
    return left_max2 + right_max2;
}

如有错误之处,敬请指出!大家共勉!

你可能感兴趣的:(Algorithm,C++,LeetCode)