连续子数组最大和

  题目来源:《剑指offer》面试题31、《编程之美》2.14

  题目:输入一个整形数组,数组里有正数也有负数。数组中一个或连续多个整数组成一个子数组。求所有子数组的和的最大值

  解法一:假设id代表自序列的一个起点,j代表终点。如果a[i]是负的,那么它不可能代表最优子序列的起点,因为任何包含a[i]的作为起点的子序列都可以通过a[i+1]作起点得到改进。类似地,任何负的子序列不可能是最优子序列的前缀。比如有数组{1,-2,3,10,-4,7,2,-5}。我们试着从头道尾逐个累加数组中的每个数字。初始化和为0。第一步加上第一个数字1,此时和为1.接下来第二步加上数字-2,和就变成了了-1.第三步加上数字3.我们注意到由于此前累计的和是-1,小于0,那如果用-1加上3,得到的和是2,比3本身还小。也就是说从第一个数字开始的子数组的和会小于从第三个数字开始的子数组的和。因此我们不用考虑从第一个数字开始的子数组,之前累加的和也就被抛弃。

  时间复杂度O(n)

bool g_invalidinput = false;

int GetMaxSum(int numbers, int len) {
    if (numbers  == NULL || len <= 0) {
        g_invalidinput = true;
        return 0;
    }

    g_invalidinput = false;

    int max_sum = INT_MIN;
    int current_sum = 0;

    for (int i = 0; i < len; i++) {
        if (current_sum <= 0) {
            current_sum = numbers[i];
        } else {
            current_sum += numbers[i];
        }

        if (current_sum > max_sum)
            max_sum = current_sum;
    }

    return max_sum;
}

解法二:分治方法。假定我们要寻找子数组A[low,high]的最大子数组。使用分治技术意味着我们要将子数组划分为两个规模尽量相等的子数组。也就是说,找到子数组的中央位置,比如mid,然后考虑求解两个子数组A[low..mid]和A[mid+1..high]。A[low..high]的在任何连续子数组A[i..j]所处的位置必然是以下三种情况之一:

  1. 完全位于子数组A[low..mid]中,因此low<=i<=j<=mid

  2. 完全位于子数组A[mid+1..high]中,因此mid<i<=j<=high

  3. 跨越了中点,因此low<=i<=mid<j<=high。

  因此,A[low..high]的一个最大子数组所处的位置必然是这三种情况之一。实际上,A[low..high]的一个最大子数组必然是完全位于A[low..mid]中、完全位于A[mid+1..high]中或者跨越了中点的所有子数组中和最大者。我们可以递归地求解A[low..mid]和A[mid+1..high]的最大子数组,因为这两个自问题仍然是最大子数组问题,只是规模更小。因此,剩下的全部工作就是寻找跨越中点的最大子数组,然后在这三者中选取和最大者。

  时间复杂度O(nlgn)

bool g_invalid_input = false;
int MaxSubArray(int numbers, int len) {
    if (numbers == NULL || len <= 0) {
        g_invalid_input = true;
        return 0;
    }

    g_invalid_input = false;

    return MaxSubArray(numbers, 0, len - 1);
}

int MaxSubArray(int *numbers, int start, int end) {
    if (start == end) {
        return a[start];
    }

    int mid = start + (end - start) / 2;

    int left_max_subarray = MaxSubArray(numbers, start, mid);
    int right_max_subarray = MaxSubArray(numbers, mid + 1, end);

    int crossing_subarray = INT_MIN;

    int left_sum = INT_MIN;
    int current_sum = INT_MIN;
    for (int i = mid; i >= start; i--) {
        current_sum += numbers[i];
        if (current_sum > left_sum)
            left_sum = current_sum;
    }

    int right_sum = INT_MIN;
    current_sum = INT_MIN;
    for(int i = mid + 1; i <= end; i++) {
        current_sum += numbers[i];
        if (current_sum > right_sum)
            right_sum = current_sum;
    }

    int crossing_sum = left_sum + right_sum;

    if (left_max_subarray >= crossing_sum && left_max_subarray >= right_max_subarray)
        return left_max_subarray;
    else if (right_max_subarray >= crossing_sum && right_max_subarray >= crossing_sum)
        return right_max_subarray
    else
        return crossing_sum;
}

参考资料:

  1. 《剑指offer》  电子工业出版社

  2. 《编程之美》   电子工业出版社

  3. 《算法导论》4.1 最大子数组问题  机械工业出版社

  4. 《数据结构与算法分析 C++描述》(第三版)P42   人民邮电出版社

时间: 2024-12-07 04:49:46

连续子数组最大和的相关文章

Maximum Subarray 连续子数组最大和

Find the contiguous subarray within an array (containing at least one number) which has the largest sum. For example, given the array [−2,1,−3,4,−1,2,1,−5,4],the contiguous subarray [4,−1,2,1] has the largest sum = 6. More practice: If you have figur

连续子数组最大和(转)

求一个数组的相加和最大的连续子数组 思路: 一直累加,只要大于0,就说明当前的“和”可以继续增大, 如果小于0了,说明“之前的最大和”已经不可能继续增大了,就从新开始, result=max{result+arr[i],arr[i]};显然,若result>0,则可以继续相加,否则,就重新开始. #include<stdio.h> #define INF 65535 int Maxsum(int * arr, int size) { int maxSum = -INF; int sum

经典算法——连续子数组最大和问题

Find the contiguous subarray within an array (containing at least one number) which has the largest sum. For example, given the array [?2,1,?3,4,?1,2,1,?5,4], the contiguous subarray [4,?1,2,1] has the largest sum = 6. 一.问题描述:输入一个整数数组,求数组中连续的子数组使其和最大

一维数组连续子数组最大和

题目描述 输入一个整型数组,数组里有正数也有负数.数组中一个或连续的多个整数组成一个子数组.求所有子数组的和的最大值. 输入描述 输入有多组数据,每组测试数据包括两行.第一行为一个整数n(0<=n<=100000),当n=0时,输入结束. 接下去的一行包含n个整数(我们保证所有整数属于[-1000,1000]). 输出描述 对应每个测试案例,需要输出一个整数单独一行,表示连续子向量的最大和. 输入样例 3 -1 -3 -2 输出样例 -1 测试代码 1 #include <stdio.h

动态规划求解连续子数组最大和问题(应该是新的描述方法?)

刚才看了下网上搜索到的TOP5使用动态规划解决此问题的代码,感觉没有突出动态规划的特点.所以自己思考了一番,提出如下解决方案: 首先再重复下动态规划的定义:将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息. 思考状态转移方程:设d[i]表示i个数形成的数组中最大的连续子数列和,数组arr[i] ,i=0...n-1; 那么d[i] 和 d[i-1]应该具备什么样的关系?显然,有两种情况, 一是arr[i]>0得到d[i] = max(

剑指offer第31题 连续子数组最大和

#include<iostream> using namespace std; int maxsum(int *list,int i) { if(list==NULL||i<=0) return -1; int cursum=0; int maxsum=0;//假设最小可以是0个数字 for(int j=0;j<i;j++) { if(cursum<=0) cursum=list[j]; else cursum=cursum+list[j]; if(cursum>max

【剑指offer】连续子数组最大和

思路dp很清楚,就是要注意细节. int FindGreatestSumOfSubArray(vector<int> array) { if(array.empty()) return 0; int sum = array[0], tempsum = array[0]; //注意初始值 不能设为0 防止只有负数 for(int i = 1; i < array.size(); i++) //从1开始 因为0的情况在初始化时完成了 { tempsum = (tempsum < 0)

剑指Offer29 连续子数组最大和

1 /************************************************************************* 2 > File Name: 29_GreatestSumOfSubArray.c 3 > Author: Juntaran 4 > Mail: [email protected] 5 > Created Time: 2016年09月01日 星期四 20时37分22秒 6 *****************************

最大连续子数组和最大、最小,绝对值最大、最小

连续子数组最大和,可考虑 public class Solution {     public int maxSubArray(int[] nums) {         int len=nums.length;         if(nums==null || len==0)return 0;         int MAX=nums[0];         int curSum=nums[0];         for(int i=1;i<len;i++){             if(c