分而治之篇-最大子数组和

问题描述

定义:最大子数组和:

int 数组中(i, j) i <= j 下标的和最大值

算法简介

一、暴力枚举法

毫无技巧,三重循环,时间复杂度为 O(n^3)

二、优化枚举法

点点技巧,两层循环,时间复杂度为 O(n^2)

三、分而治之法

不断二分,记左边数组的最大子数组和为 s 1 s_1 s1 ,右边数组最大子数组和为 s 2 s_2 s2 ,跨中间区域的最大子数组和为 s 3 s_3 s3

则整个数组的最大子数组和为:
s = m a x ( s 1 , s 2 , s 3 ) s = max(s_1, s_2, s_3) s=max(s1,s2,s3)
时间复杂度为 O(nlogn)

最大子数组和函数

int MaxSubArray(int left, int right, int array[])

二分递归求出左右数组的最大子数组和,若 left == right 则返回

跨中间区域的最大子数组和

int CrossingSubArray(int left, int mid, int right, int array[])

跨中间区域的最大子数组和特征:一定包含 array[mid],由此求出 s 3 s_3 s3 ,时间复杂度为 O(n)

四、动态规划法

时间复杂度:O(n)

数学依据:记 D[i] 为以 i 为开头的最大子数组和,一定包含 array[i],则:
s = m a x ( D [ 0 , n − 1 ] ) s = max(D[0 , n-1]) s=max(D[0,n1])
递推关系:
D [ i ] = { D [ i + 1 ] + a r r a y [ i ] D [ i + 1 ] > 0 a r r a y [ i ] D [ i + 1 ] < 0 D[i] = \begin{cases} D[i+1] + array[i] & D[i+1] >0 \\ array[i] & D[i+1] < 0 \\ \end{cases} D[i]={D[i+1]+array[i]array[i]D[i+1]>0D[i+1]<0

代码实现

// 此程序用于计算一个int数组中的最大子数组和
// 即int数组中(i, j) i <= j 下标的和最大值
#include <iostream>
#include <cstdio>
using namespace std;


int MaxSubArray(int left, int right, int array[]);
int CrossingSubArray(int left, int mid, int right, int array[]);
int MaxSubArrayDP(int array[], int end, int rec[]);

int main(void) {
    int Array[100] = {0};   // 原数组, 简单起见不要超过100
    int capacity;           // 数组容量
    cout << "输入数组的容量:";
    cin >> capacity;
    for(int i=0; i < capacity; i++) cin >> Array[i];

// 暴力枚举法  
// 时间复杂度 O(n^3)

    int max1 = -999999999;
    for(int i=0;i<capacity;i++) {
        for(int j=i;j<capacity;j++) {
            int total = 0;
            for(int k=i;k<=j;k++)  total += Array[k];
            max1 = (total > max1) ? total : max1;
        }
    }
    cout << max1 << endl;

// 优化枚举法
// 时间复杂度 O(n^2)

    int max2 = -999999999;
    for(int i=0;i<capacity;i++) {
        int total = 0;
        for(int j=i;j<capacity;j++) {
            total += Array[j];
            max2 = (total > max2) ? total : max2;
        }
    }
    cout << max2 << endl;

// 分而治之法
// 时间复杂度 O(nlogn)

    int max3;
    max3 = MaxSubArray(0, capacity-1, Array);
    cout << max3 << endl;

// 动态规划法
// 时间复杂度 O(n)

    int max4;
    int Rec[capacity] = {0};
    max4 = MaxSubArrayDP(Array, capacity - 1, Rec);
    cout << max4 << endl;

    return 0;
}


int CrossingSubArray(int left, int mid, int right, int array[]) {
    int maxLeft = -999999999;
    int maxRight = -999999999;
    int addLeft = 0, addRight = 0;
    for(int i=mid;i>=left;i--) {
        addLeft += array[i];
        maxLeft = (addLeft > maxLeft) ? addLeft : maxLeft;
    } 
    for(int i=mid+1;i<=right;i++) {
        addRight += array[i];
        maxRight = (addRight > maxRight) ? addRight : maxRight;
    }
    return maxLeft + maxRight;
}

int MaxSubArray(int left, int right, int array[]) {
    if(left == right) return array[left];
    else {
        int s1, s2, s3;
        int mid = (left + right) / 2;
        s1 = MaxSubArray(left, mid, array);              // 左侧数组的最大子数组
        s2 = MaxSubArray(mid + 1, right, array);         // 右侧数组的最大子数组
        s3 = CrossingSubArray(left, mid, right, array);  // 跨中间区域的最大子数组
        return (s1 > s2 && s1 > s3) ? s1 :
               (s2 > s1 && s2 > s3) ? s2 : s3;
    }
}

int MaxSubArrayDP(int array[], int end, int rec[]) {
    // 数学依据
    // 定义 Di 为以i为起点的最大子数组
    // 则:若 Di+1 > 0 ,Di = Di+1 + array[i]
    //     若 Di+1 < 0 ,Di = array[i]
    // 用 Rec[] 记录最大子数组的终止位置

    int max = array[end];
    int D[end + 1] = {0};
    for(int i = end; i >= 0; i--) {
        if(i == end) {
            D[end] = array[end];
            rec[end] = end;
        } else {
            if(D[i + 1] > 0) {
                D[i] = array[i] + D[i + 1];
                rec[i] = rec[i + 1];
            } else {
                D[i] = array[i];
                rec[i] = i;
            }
            max = (D[i] > max) ? D[i] : max;
        }
    }
    return max;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值