第四章 分治策略——最大子数组问题

最大子数组问题

方法一:暴力求解方法

我们可以很容易地设计出一个暴力方法来求解本问题:简单地尝试没对可能的子数组,共有O(n2)种

#include<iostream>
using namespace std;
#define INT_MIN 0x80000000
int main()
{
    int arr[10]={9,8,-3,-5,7,-39,79,-37,8,9};
    int i,j;
    int sum=0,maxsum=INT_MIN;
    int imax;
    for(i=0;i<10;i++)
    {
        sum=0;
         for(j=i;j<10;j++)
         {
            sum+=arr[j];
            if(maxsum<sum)
            {
                maxsum=sum;
                imax=j;
            }
         }
    }

    cout<<"maxsum: "<<maxsum<<" imax:"<<imax<<endl;
}

方法二:使用分治策略的求解方法(O(nlgn))

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

  • 完全位于子数组A[low..mid]中,因此low<=i<=j<=mid
  • 完全位于子数组A[mid+1..high]中,因此mid<i<=j<=high;
  • 跨越了中点,因此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]的最大子数组,因为这两个子问题仍然是最大子数组的问题,只是规模更小。因此,剩下的全部工作就是寻找跨越中点的最大子数组,然后在三种情况中选取和最大者。

算法如下:

#include<iostream>
#include<tuple>
using namespace std;
#define INT_MIN 0x80000000
tuple<int,int,int> FindMaxCrossingSubarry(int arr[],int low,int mid,int high)
{
    int leftsum=INT_MIN,rightsum=INT_MIN;
    int sum=0;
    int maxleft=low,maxright=mid+1;
    int i;
    for(i=mid;i!=low;i--)
    {
        sum+=arr[i];
        if(leftsum<sum)
        {
            leftsum=sum;
            maxleft=i;
        }
    }
    sum=0;
    for(i=mid+1;i!=high;i++)
    {
        sum+=arr[i];
        if(rightsum<sum)
        {
            rightsum=sum;
            maxright=i;
        }
    }
    cout<<"maxleft: "<<maxleft<<"  maxright: "<<maxright<<"  leftsum+rightsum: "<<leftsum+rightsum<<endl;
    return tuple<int,int,int>(maxleft,maxright,leftsum+rightsum);
}

tuple<int,int,int> FindMaximumSubarry(int arr[],int low,int high)
{
    if(low==high)
        return tuple<int,int,int>(low,high,arr[low]);
    else
    {
        int mid=(low+high)/2;
        tuple<int,int,int> left=FindMaximumSubarry(arr,low,mid);
        tuple<int,int,int> right=FindMaximumSubarry(arr,mid+1,high);
        tuple<int,int,int> middle=FindMaxCrossingSubarry(arr,low,mid,high);
        if(get<2>(left)>=get<2>(right)&&get<2>(left)>=get<2>(middle))
            return left;
        else if(get<2>(right)>=get<2>(left)&&get<2>(right)>=get<2>(middle))
            return right;
        else
            return middle;
    }
}

int main()
{
    int arr[10]={9,8,-3,-5,7,-39,79,-37,8,9};
    tuple<int,int,int> result=FindMaximumSubarry(arr,0,9);
    cout<<get<0>(result)<<" "<<get<1>(result)<<" "<<get<2>(result)<<endl;
}

方法三:使用动态规划的算法(O(n))

#include<iostream>
using namespace std;

int MaxArraySum(int arr[],int n)
{
    int sum,maxSum,maxi;
    int i;
    maxSum=0;
    maxi=0;
    sum=0;
    for(i=0;i<n;i++)
    {
        sum+=arr[i];
        if(sum<0)
        {
            sum=0;
            continue;
        }
        if(maxSum<sum)
        {
            maxSum=sum;
            maxi=i;
        }
    }
    cout<<"max sum: "<<maxSum<<" index: "<<maxi<<endl;
    return maxSum;
}

int main()
{
    int arr[10]={9,8,-3,-5,7,-39,79,-37,8,9};
    cout<<MaxArraySum(arr,10);
}
时间: 2024-10-12 16:40:07

第四章 分治策略——最大子数组问题的相关文章

第四章 分治策略 4.1 最大子数组问题 (暴力求解算法)

/** * 最大子数组的暴力求解算法,复杂度为o(n2) * @param n * @return */ static MaxSubarray findMaxSubarraySlower(int[] n) { long tempSum = 0; int left = 0; int right = 0; long sum = Long.MIN_VALUE; for (int i = 0; i < n.length; i++) { for (int j = i; j < n.length; j++

第四章 分治策略 4.1 最大子数组问题 (减治法,别人的,拿来看看)

/** * 获得连续子数组的最大和 * * @author dfeng * */ private static long getMax(long a, long b) { return a > b ? a : b; } /** * 获得连续子数组的最大和 * * @param array * @return 最大和,此处用了Long型是为了表示当参数为null或空时,可以返回null,返回其它任何数字都可能引起歧义. */ public static Long getMax(int[] arra

第四章 分治策略 4.1 最大子数组问题(自己想的,不知道是不是减治法)

package chap04_Divide_And_Conquer; import static org.junit.Assert.*; import java.util.Arrays; import org.junit.Test; /** * 算反导论第四章 4.1 最大子数组 * * @author xiaojintao * */ public class Maximum_Subarray_Problem { /** * 最大子数组类 left为头部索引,right为尾部索引,sum为数组和

分治策略 &nbsp; 最大子数组问题

递归式 递归式与分治方法是紧密相关的,因为使用递归式可以很自然地刻画分治算法的运行时间.一个递归式就是一个等式或不等式,它通过更小的输入上的函数值来描述一个函数.例如,在2.3.2节,我们用递归式描述了MERGE-SORT过程的最坏情况运行时间T(n): Θ(1)        若n=1 T(n) =                         (4.1) 2T(n/2)+Θ(n)    若n>1 求解可得T(n)=Θ(nlgn) 递归式可以有很多形式.例如,一个递归算法可能将问题划分为规模

算法导论第四章分治策略编程实践(二)

在上一篇中,通过一个求连续子数组的最大和的例子讲解,想必我们已经大概了然了分治策略和递归式的含义,可能会比较模糊,知道但不能用语言清晰地描述出来.但没关系,我相信通过这篇博文,我们会比较清楚且容易地用自己的话来描述. 通过前面两章的学习,我们已经接触了两个例子:归并排序和子数组最大和.这两个例子都用到了分治策略,通过分析,我们可以得出分治策略的思想:顾名思义,分治是将一个原始问题分解成多个子问题,而子问题的形式和原问题一样,只是规模更小而已,通过子问题的求解,原问题也就自然出来了.总结一下,大致

算法导论 第四章 分治策略

分治策略中,我们递归地求解了一个问题,在每层递归都应用了三步 1.分解,将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小 2.解决,递归地求解出子问题,如果子问题的规模足够小,则停止递归,直接求解 3.合并,把子问题的解给合并为原问题的解 当子问题足够大的时候,需要递归,那就是递归情况 当问题足够小的时候,不需要递归,那就是基本情况 三种求解递归式的方法:代入法 猜测一个界,用数学归纳法来证明这个界 递归树法 将递归式转化为一棵树,其节点表示不同层次的递归调用产生的代价,然后采用边

第四章 分治策略 4.2 矩阵乘法的Strassen算法

package chap04_Divide_And_Conquer; import static org.junit.Assert.*; import java.util.Arrays; import org.junit.Test; /** * 矩阵相乘的算法 * * @author xiaojintao * */ public class MatrixOperation { /** * 普通的矩阵相乘算法,c=a*b.其中,a.b都是n*n的方阵 * * @param a * @param b

【算法设计-分治】最大子数组问题

1.问题描述 算法导论上38页,最大子数组给出一组数组来,然后求出使得从i到j的数组值的和最大的边界i与j.如果不用分治的方法的话,需要通过暴力方法来寻找n(n-1)/2种. 方法: 2代码: #include<iostream> using namespace std; typedef struct maximum { int left; int right; int sum; }Maximum; Maximum* FIND_MAX_CROOSING_SUBARRAY(int *A,int

分治算法——最大子数组

表示很久没有接触算法了,好多东西真心要一点点拾掇起来,为了找份好工作,我也是蛮拼的了. 好吧,下来说说分治算法,因为在leetcode上刚好碰到这么个问题,想到要用分治做,但是一时又不清楚具体步骤.于是抱起<算法导论>啃起来.刚好上面的例子也是这个算法,就研读了一下. 假定,我们要寻找子数组A[low...high]的最大字数组,使用分治算法,其结果必定是以下三种情况中的一个: 1.完全位于子数组A[low...mid]中,因此low <= i <= j <= mid 2.完