算法导论--寻找最大子数组

问题

求最大子数组

分析

  • 第一种 暴力求解 Θ(n2)
    此种方法通过两层循环,去寻找最大子数组,思路上很清晰,以i序列的数为起点,求此后的各个子序列的和,比较大小,保留最大的和和起止序列;但此方法的时间为 Θ(n2) ,当遇到规模较大的输入时,计算将相当缓慢;
  • 第二种 递归求解 Θ(nlgn)
    递归常用于分治算法,将一个小的问题拆分成一个个规模较小的问题(一般来说子问题与父问题是同样的问题,只是规模更小),直到不符合拆分条件,再各个解决子问题;从解决子问题的顺序来看,一般是从根节点开始,先是左半边问题全部解决后(取决于你的代码怎样写),再解决右半部分;
    在此问题中,寻找最大子数组,最大子数组必然存在于数组左半部分或者右半部分或者跨域中间部分;这样就用这三部分的最大子数组的和来比较,得出整个数组的最大子序列;递归时,每一层的问题都是相同的,先找出每一层各个子序列的最大子序列,这即为上一层的左或者右部分最大子序列,在寻找上层的中间最大子序列,就会得出上一层的最大子序列;以此类推,找出整个数组的最大子序列;
#include <iostream>
using namespace std;

struct Result_Structure{
    int sum;
    int low_index;
    int high_index;
};

Result_Structure mid_array_sum(int*a,int low,int mid,int high){   //寻找中间最大子序列
    Result_Structure result;
    int temp_left_sum = a[mid];
    int temp_right_sum = a[mid + 1];
    int sum = 0;
    result.low_index = mid;
    for (int i = mid; i >= 0; i--) {     //左半部分求和
        sum += a[i];
        if(temp_left_sum < sum){
            temp_left_sum = sum;
            result.low_index = i;
        }
    }

    sum = 0;
    result.high_index = mid + 1;
    for (int i = mid + 1; i <= high; i++) {  //右半部分求和
        sum += a[i];
        if(temp_right_sum < sum){
            temp_left_sum = sum;
            result.high_index = i;
        }
    }

    result.sum = temp_left_sum + temp_right_sum;
    return result;
}

Result_Structure recursive_merge(int*a,int low,int high){
    Result_Structure result,result_left,result_right,result_mid;
    if(low == high){                            //不符合划分条件,直接返回
        result.high_index = high;
        result.low_index = low;
        result.sum = a[low];

        return result;
    }

    int mid = (low + high)/2;
    result_left = recursive_merge(a,low,mid);           //递归左半部分
    result_right = recursive_merge(a,mid+1,high);       //递归右半部分
    result_mid  = mid_array_sum(a,low,mid,high);        //计算此层的和(并不是最大中间子序列,问题已经化解为三部分找最大部分)

    if(result_left.sum > result_right.sum && result_left.sum > result_mid.sum){     //比较大小返回三者中最大的
        return result_left;
    }else if(result_right.sum > result_left.sum && result_right.sum > result_mid.sum){
        return result_right;
    }else{
        return result_mid;
    }

    return result;
}


int main() {
    int a[4] = {1,2,3,4};
    Result_Structure result = recursive_merge(a,0,3);
    cout << "sum = " << result.sum <<endl
            << "low_index = " << result.low_index << endl
            << "high_index = " << result.high_index << endl;
    return 0;
}

  • 第三种 最优解 Θ(n)
    此种方法具有最优的时间复杂度 Θ(n) ;基本的原理是:
    当当前最大值小于零时,丢弃;大于等于零时,不断累加;比较先前的最大值和当前和,当前和大时,更新最大子序列和值和索引;
#include <iostream>
#include <algorithm>
using namespace std;

struct Result_Struct{     //结果返回结构体
    int low_index;
    int high_index;
    int max_sum;
};

Result_Struct find_max_array_best(int* a,int low,int high){
    Result_Struct result;
    result.max_sum = a[0];              //初始化结果结构体
    result.low_index = 0;
    result.high_index = 0;
    int temp_low_index = 0;             //初始化临时索引记录变量
    int temp_high_index = 0;
    int current_sum = 0;                //临时结果
    for (int i = 0; i < (high - low + 1); ++i) {
        if(current_sum < 0){
            current_sum = a[i];         //当每一次重新开始统计最小值时,临时索引变为当前的i值
            temp_low_index = i;
            temp_high_index = i;
        }else{
            current_sum += a[i];        //统计值大于等于0,继续叠加,temp_high_index 为当前索引
            temp_high_index = i;
        }
        if(current_sum > result.max_sum){       //变更最大值,同时更新索引
            result.max_sum = current_sum;
            result.low_index = temp_low_index;
            result.high_index = temp_high_index;
        }
    }
    return result;
}

int main() {
    int a[4] = {-1,-2,3,4};
    Result_Struct result = find_max_array_best(a,0,3);
    cout << "sum = " << result.max_sum <<endl
            << "low_index = " << result.low_index << endl
            << "high_index = " << result.high_index << endl;
    return 0;
}

总结

当前存在的就是这三种方法:

  • 第一种方法 思路简单,适合小规模的输入;
  • 第二种方法 对于此问题处在尴尬的位置,但是是处理问题的一种更为普遍的方法,能应用在许多地方,例如排序;
  • 第三种方法 对于此问题最为简单,只有 Θ(n) 的时间复杂度,更适合此
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值