最大子数组

非常经典的问题了,大题意思就是一串数组里面有正有负,求加起来最大的子数组。例如:
{13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7}
最大子数组为:
{18,20,-7,12}
我在这里只是为了学习分治,所以那个复杂度O(n^2)的那个就不再多说。直接进入正题,用分治来解决。
分析:
三种情况:
1.完全位于子数组A[left,mid],i,j∈[left,mid];
2.完全位于子数组B[mid+1,right],i,j∈(mid,right];
3.跨越中点,i∈[left,mid];j∈(mid,right];
可以将问题分为最终左右数组大小为1的情况,然后合并,这样层层递归。

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int Max(int a, int b, int c) {
    return a > b ? a > c ? a : c : b > c ? b : c;
}
int go(int *List, int left, int right) {
    int maxleft, maxright;
    int maxleft_1, maxright_1;
    int left_1, right_1;
    int mid, i;
    if (left == right) {//这里就是底层了,不要判定正负,我加了反正是不出结果,毕竟只有一个数的情况下,这个数就是最大值。
        return List[left];
    }
    mid = (left + right) / 2;
    maxleft_1 = 0, maxright_1 = 0, left_1 = 0, right_1 = 0;
    for (i = mid; i >= left; i--) {
        left_1 += List[i];
        if (left_1 > maxleft_1)
            maxleft_1 = left_1;
    }
    for (i = mid + 1; i <= right; i++) {
        right_1 +=List[i];  
        if (right_1 > maxright_1)
            maxright_1 = right_1;
    }
    maxleft = go(List, left, mid);
    maxright = go(List, mid + 1, right);
    return Max(maxleft, maxright, maxleft_1 + maxright_1);
}
int main(void) {
    int n; cin >> n;
    int *List = new int[n];
    for (int i = 0; i < n; i++)
        cin >> List[i];
    cout<<go(List, 0, n - 1);
    system("pause");
    return 0;
}

还有种方法是动态规划,一趟过的那种,这里我在加一条记录起始最大子数组起始位置的条件

#include <iostream>  

using namespace std;  

int FindMaxSubarray(int array[], int length)  
{  
    int start = 0, end = 0;  //记录最大子数组的起始位置(在数组中的下标)  
    int MaxSumSub;  //最大子数组的值  
    int* dp = new int[length];  //动态规划记录  

    dp[0] = array[0];  //初始为第一个数  
    MaxSumSub = dp[0];  //最大值初始为第一个数  
    int temp = 0;  //  

    for(int i = 1; i < length; i++)  
    {  
        if(dp[i - 1] <= 0)  //前面的<0,直接丢弃  
        {  
            dp[i] = array[i];  
            temp = i; //记录起始为止  
        }  
        else  
            dp[i] = array[i] + dp[i - 1];  //往后求和  

        if(dp[i] > MaxSumSub)  //找到到i为止的最大子数组  
        {  
            MaxSumSub = dp[i];  //最大...  
            start = temp;  //标记起始  
            end = i;  //标记此时的结束位置  
        }  
    }  

    cout<<"最大子序列的下标:"<<start<<"->"<<end<<endl;  
    return MaxSumSub;  

}  

int main()  
{  
    int a[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};  
    //int a[] = {23, 4};  
    int length = sizeof(a) / sizeof(int);  

    cout<<FindMaxSubarray(a, length);  
    return 0;  
}  
### 最大子数组和算法的实现 最大子数组和问题的目标是找到一个数组中和最大的连续子数组。该问题在算法领域中具有重要意义,尤其在动态规划和分治算法的应用中被广泛研究。 #### 方法1:蛮力法 蛮力法是最直观的解决方案,其基本思想是遍历所有可能的子数组,计算它们的和,并从中找出最大值。这种方法的时间复杂度为 $O(n^2)$,对于较小的数组规模是可行的,但对于较大的数组则效率较低。 以下是C语言实现: ```c #include <stdio.h> int maxSubArray1(int a[], int n) { int maxSum = 0; int i, j; for (i = 0; i < n; i++) { int currentSum = 0; for (j = i; j < n; j++) { currentSum += a[j]; if (currentSum > maxSum) { maxSum = currentSum; } } } return maxSum; } int main() { int arr[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("最大子数组的和为: %d\n", maxSubArray1(arr, n)); return 0; } ``` #### 方法2:动态规划法 动态规划法是解决最大子数组和问题的经典方法,其时间复杂度为 $O(n)$,效率显著优于蛮力法。其核心思想是维护一个当前子数组的和,如果当前子数组的和小于0,则丢弃之前的子数组并重新开始计算。 以下是C语言实现: ```c #include <stdio.h> int maxSubArray2(int a[], int n) { int maxSum = a[0]; int currentSum = a[0]; for (int i = 1; i < n; i++) { currentSum = (currentSum + a[i] > a[i]) ? currentSum + a[i] : a[i]; maxSum = (currentSum > maxSum) ? currentSum : maxSum; } return maxSum; } int main() { int arr[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("最大子数组的和为: %d\n", maxSubArray2(arr, n)); return 0; } ``` #### 方法3:分治法 分治法将数组分为左右两部分,分别求解左半部分和右半部分的最大子数组和,同时考虑跨越中间的最大子数组和。这种方法的时间复杂度为 $O(n \log n)$,适合大规模数据的处理。 以下是C语言实现: ```c #include <stdio.h> // 求三者中的最大值 int max(int a, int b, int c) { return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); } // 求跨越中间的最大子数组和 int maxCrossingSum(int a[], int left, int mid, int right) { int sum = 0; int leftSum = -999999; for (int i = mid; i >= left; i--) { sum += a[i]; if (sum > leftSum) { leftSum = sum; } } sum = 0; int rightSum = -999999; for (int i = mid + 1; i <= right; i++) { sum += a[i]; if (sum > rightSum) { rightSum = sum; } } return leftSum + rightSum; } // 分治法主函数 int maxSubArray3(int a[], int left, int right) { if (left == right) { return a[left]; } int mid = (left + right) / 2; int leftMax = maxSubArray3(a, left, mid); int rightMax = maxSubArray3(a, mid + 1, right); int crossMax = maxCrossingSum(a, left, mid, right); return max(leftMax, rightMax, crossMax); } int main() { int arr[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("最大子数组的和为: %d\n", maxSubArray3(arr, 0, n - 1)); return 0; } ``` ### 总结 - **蛮力法**:简单直观,但效率较低,时间复杂度为 $O(n^2)$。 - **动态规划法**:高效且简洁,时间复杂度为 $O(n)$。 - **分治法**:适用于大规模数据,时间复杂度为 $O(n \log n)$,但实现较复杂。 根据具体需求和数据规模,可以选择合适的算法实现最大子数组和的求解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值