个人学习——《算法导论》 第4章 4.1最大子数组问题 个人笔记

本文对比了三种求解数组中连续子序列和最大值的方法:穷举法、分治法(递归实现)和动态规划。穷举法计算复杂度高,分治法时间复杂度为nlogn,而动态规划通过存储和更新子问题结果,效率提升。通过C语言实例演示了每种方法的代码实现。

要求:
给出一个数组,求出其中连续的,和最大的子序列

方法一 穷举

假定数组中有n个数字,将所有的可能列出来逐一比较取最大值
要进行n∗(n−1)2次的求和计算要进行\frac{n*(n-1)}{2} 次的求和计算 2n(n1)

C语言的实现

#include<stdio.h>
#include<limits.h>
int main()
{
    int arr[] = {1,-2,3,4,-5, 6, -7, 6};
    int sum = INT_MIN;
    int i , j, k = sizeof(arr) / sizeof (arr[0]) ;
    for(i = 0; i < k; i++)
    {   
        int temp = 0;
        for(j = i;j < k; j++)
        {
            temp += arr[j];
            
            if (temp > sum)
            {sum = temp;}
        }
    } 
    printf("%d",sum);
}

输出为 8

方法二 分治法

我们可以将该待处理的数组进行二分
该数组 int arr[] = {1,-2,3,4}可以从中间分开
拆分成
{1,-2} {3,4}
再次将两个子列拆分成
{1} {-2} {3} {4}
首先从{1}和{-2}进行处理,由于目的要求得到连续的子列,于是对{1,-2}这个子序列我们可以得到“1” “-2” “-1”这三种和,其中“-2”和“-1”是可以和后续的{3,4}进行相加的,而1要单独考虑是否大于后续的子集和

相比较方法一的穷举用时与n²成正比。使用分治方法所处理的时间和nlogn成正比

C语言代码实现

#include<stdio.h>

int divide(int arr[],int L,int R)
{
    if(L == R){return arr[L];}//只有一个数的话最大值只能是他本身

    int mid = (L + R) / 2;

    int L_max = divide(arr, L, mid);//在mid左半部最大的子列
    int R_max = divide(arr, mid + 1, R);//右半部最大子列
    
    int i,L_sum = 0, L_temp = 0;

    for(i = mid; i >= L; i--)//与mid有联系的左半部分求和
    {   
        L_sum += arr[i];
        if(L_sum > L_temp) L_temp = L_sum;
    }

    int R_sum = 0, R_temp = 0;
    for(i = mid + 1; i <= R; i++)//右半部分
    {
        R_sum += arr[i];
        if(R_sum >= R_temp) R_temp = R_sum;
    }

    
    int A_sum = R_temp + L_temp;//与mid有关的最大子列
    
    if(A_sum > L_max && A_sum > R_max){return A_sum;}
    else if(L_max > A_sum && L_max > R_max){return L_max;}
    else return R_max;

}


int main()
{
    int arr[] = {1,-2,3,4,-5, 6, -7, 6};
    int sum = INT_MIN;
    int i , j, k = sizeof(arr) / sizeof (arr[0]) ;
    
    printf("%d",divide(arr,0,k-1));//注意在进行递归的时候R的大小可能会越界,所以要减一
}

方法三 动态规划法

开两块区域
由于我们要连续的,加起来和最大的子列
那么假定第n个数字是3,前面n-1个数字的和却是一个负数,那么我们自然是没有加前面n-1个数字的必要的。
同时我们假定前n-1项中,已经找到了一个子列,和为5,这样子我们就可以从第n项开始继续往后加来寻找是否可能存在一个新的子列和可以大于5

第一个区域用于储存第 i 项到第 j 项之间的和,当 i 到 j 的项的和变为负数的时候,i = j +1; j 继续增大,然后继续储存 i 到 j 之间的整数和

然后对 每次进行依次区域1储存后的最大值和区域二已有的最大值进行比较,取两者中较大值继续储存在区域二中(也就是说区域二是一个一直不递减的数列)

C语言代码实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int dynamic(int arr[], int L, int R)
{
    int *p1 = (int *)malloc(sizeof(int) * R);
    int *p2 = (int *)malloc(sizeof(int) * R);
    memset(p1,0,sizeof(int)*R);
    memset(p2,0,sizeof(int)*R);
    *p1 = arr[L];
    *p2 = arr[L];

    int temp = *p1, i;
    for(i = L + 1; i < R; i++)
    {
        temp += arr[i];
        p1[i] = (temp > 0) ? temp : p1[i - 1];
        p2[i] = (p1[i] > p2[i - 1]) ? p1[i] : p2[i - 1];
        
        if (temp < 0) temp = 0;
    }
    return p2[R - 1];//注意越界问题
}


int main()
{
    int arr[] = {1,-2,3,4,-5, 6, -7, 6};
    
    int k = sizeof(arr) / sizeof (arr[0]) ;
    
    printf("%d",dynamic(arr,0,k));/
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值