楼主这篇文章的目的是要带大家梳理一下,有关于求子数组问题。如求子数组的最大和,求最大和的子数组,求最大积的子数组等一系列问题。今天阳光明媚,楼主今天心情很好哦,愿大家开心每一天,哈哈。Are you ready?开始了哦~~~
题目求子数组的最大和,这里需要注意的一个问题就是,子数组那么便意味着是连续的一段数据。我们可以先写的例子,方便我们注意到要考虑的一些问题。
数组:[1, -2, 3, 5, -3, 2] 应该返回8,最大和的子数组为3,5 
数组:[0, -2, 3, 5, -1, 2] 应该返回9,最大和的子数组为3, 5,-1,2 
数组:[-1, -2, -3, -4, -5]应该返回-1,最大和的子数组为-1 
这里,我们只介绍一种时间复杂度为
将所给数组
看下面代码:
#include <iostream>  
#include <limits>
using namespace std;  
int getMAX3(int a, int b, int c);
int getBiggestSubSum(int *pArray, int low, int high);
int main()  
{  
    int a1[] = {1, -2, 3, 5, -3, 2};
    int a2[] = {1, -2, 3, 5, -1, 2};
    int a3[] = {-1, -2, -3, -5, -3, -2};
    cout << "数组1的子数子的最大和为" << getBiggestSubSum(a1, 0, sizeof(a1) / sizeof(int) - 1) << endl;
    cout << "数组2的子数子的最大和为" << getBiggestSubSum(a2, 0, sizeof(a2) / sizeof(int) - 1) << endl;
    cout << "数组3的子数子的最大和为" << getBiggestSubSum(a3, 0, sizeof(a3) / sizeof(int) - 1) << endl;
    system("pause");
}  
int getBiggestSubSum(int *pArray, int low, int high)
{
    int middle = (low + high) >> 1; //获取中间节点的下标
    int MAX = numeric_limits<int>::min();
    if (high == low)
    {
        //递归到只剩下一个数之后,那么直接返回
        return pArray[low];
    }
    int lMax = numeric_limits<int>::min(); 
    int rMax = numeric_limits<int>::min(); 
    int mMax = numeric_limits<int>::min(); 
    int mMaxTemp = 0;
    //求左半部分的最大和
    lMax = getBiggestSubSum(pArray, low, middle);
    //求右半部分的最大和
    rMax = getBiggestSubSum(pArray, middle + 1, high);
    //求横跨中间的最大和
    for (int i = middle; i <= high; i++)
    {
        mMaxTemp += pArray[i];
        mMax = max(mMax, mMaxTemp);
    }
    for (int i = middle - 1; i >= 0; i--)
    {
        mMaxTemp += pArray[i];
        mMax = max(mMax, mMaxTemp);
    }
    //返回三大部分最大和之间的最大值
    return getMAX3(rMax, mMax, lMax);
}
int getMAX3(int a, int b, int c)
{
    int max = a;
    max = (b > max) ? (b):(max);
    max = (c > max) ? (c):(max);
    return max;
}算法复杂度:
我们可以考虑数组的第一个元素
从上面三种情况可以看出来,我们可以将一个大问题,转化成一个较小的问题。如本题中,我们可以将一个N维数组的子数组最大和问题,转化成N-1维的子数组最大和问题。假设已经知道
上述公式中,第一项表示只有一个
#include <iostream>  
using namespace std;  
int getBiggestSubSum(int *pArray, int len);
int main()  
{  
    int a1[] = {1, -2, 3, 5, -3, 2};
    int a2[] = {1, -2, 3, 5, -1, 2};
    int a3[] = {-1, -2, -3, -5, -3, -2};
    cout << "数组1的子数子的最大和为" << getBiggestSubSum(a1, sizeof(a1) / sizeof(int)) << endl;
    cout << "数组2的子数子的最大和为" << getBiggestSubSum(a2, sizeof(a2) / sizeof(int)) << endl;
    cout << "数组3的子数子的最大和为" << getBiggestSubSum(a3, sizeof(a3) / sizeof(int)) << endl;
    system("pause");
}  
int getBiggestSubSum(int *pArray, int len)
{
    //从数组的最后一个元素开始,当只有一个元素的时候,那么start和all当然都是等于这个数啦
    int nStart = pArray[len - 1];
    int nAll = pArray[len - 1];
    for (int i = len - 2; i >= 0; i--)
    {
        nStart = max(pArray[i], nStart + pArray[i]);
        nAll = max(nAll, nStart);
    }
    return nAll;
}运行结果:
数组1的子数子的最大和为8
数组2的子数子的最大和为9
数组3的子数子的最大和为-1
请按任意键继续. . .算法复杂度:
题目一只管求出子数组的最大和,不管子数组是什么。而这个题目中,我们需要求解子数组序列的起始位置和结束位置。其实和上面差不多,只是需要记录位置而已,看代码,在题目一的解法二上面修改的。
#include <iostream>  
using namespace std;  
int getBiggestSubSum(int *pArray, int len, int *startIndex, int *endIndex);
void printMaxSubArray(int *pArray, int start, int end);
int main()  
{  
    int a1[] = {1, -2, 3, 5, -3, 2};
    int a2[] = {1, -2, 3, 5, -1, 2};
    int a3[] = {-1, -2, -3, -5, -3, -2};
    int start = 0;
    int end = 0;
    cout << "数组1的子数子的最大和为" << getBiggestSubSum(a1, sizeof(a1) / sizeof(int), &start, &end) << endl;
    cout << "和最大的子序列为" << endl;
    printMaxSubArray(a1, start, end);
    cout << "数组2的子数子的最大和为" << getBiggestSubSum(a2, sizeof(a2) / sizeof(int), &start, &end) << endl;
    cout << "和最大的子序列为" << endl;
    printMaxSubArray(a2, start, end);
    cout << "数组3的子数子的最大和为" << getBiggestSubSum(a3, sizeof(a3) / sizeof(int), &start, &end) << endl;
    cout << "和最大的子序列为" << endl;
    printMaxSubArray(a3, start, end);
    system("pause");
} 
int getBiggestSubSum(int *pArray, int len, int *startIndex, int *endIndex)
{
    //从数组的最后一个元素开始,当只有一个元素的时候,那么start和all当然都是等于这个数啦
    int nStart = pArray[len - 1];
    int nAll = pArray[len - 1];
    *startIndex = len - 1;
    *endIndex = len - 1;
    for (int i = len - 2; i >= 0; i--)
    {
        if (pArray[i] > nStart + pArray[i]) 
        {       
            nStart = pArray[i];
            if (nStart > nAll)
            {
                //如果仅仅包含pArray[i]的子序列获得最大和的话,那么startIndex和endIndex和nAll都需要更新
                *startIndex = i;
                *endIndex = i;
                nAll = pArray[i];
            }
            else
            {
                //还是原来的子序列和最大,那startIndex和endIndex都不变了
            }
        }
        else
        {
            nStart = nStart + pArray[i];
            if (nStart > nAll)
            {
                //如果包含a[i],a[i+1],...a[j]获得最大的和的话,那么要更新startIndex和nAll
                nAll = nStart;
                *startIndex = i;
            }
            else
            {
                //还是原来的子序列和最大,那startIndex和endIndex都不变了
            }
        }
    }
    return nAll;
}
void printMaxSubArray(int *pArray, int start, int end)
{
    for (int i = start; i <= end; i++)
    {
        cout << pArray[i] << " ";
    }
    cout << endl;
}时间复杂度同样是
这里必须要重述一下题目,和题目一以及题目二是不一样的!!!!
给定一个长度为N的整数数组,只允许用乘法,不能用除法,计算任意N-1个数的组合乘积中的最大的一组,并写出算法的时间复杂度。
这里的子数组,是任意N-1个数的组合,并不一定是连续的N-1的子序列。
其实这道题,腾讯某年笔试题的加试题就有这道题,感兴趣的同学可以看这个链接2012年腾讯实习生笔试附加题 
题目简介:已知数组a[n],求数组b[n].要求:b[i]=a[0]a[1]……*a[n-1]/a[i],不能用除法。a.时间复杂度O(n),空间复杂度O(1)。 b.除了迭代器i,不允许使用任何其它变量(包括栈临时变量等),看,是不是差不多?
这里我们同样可以使用前累乘后累乘的办法把所有的b[i]求出来,然后遍历一遍,求出最大的积。看代码:
#include <iostream>  
using namespace std;  
void getBiggestMultipleResults(int *pArray, int *pResultsArray, int len);
int main()  
{  
    int a[] = {1, 2, 3, 4};
    int len = sizeof(a) / sizeof(int);
    int *b = new int[len]();
    int result = b[0];
    //求出所有的b[i]
    getBiggestMultipleResults(a, b, len);
    for (int i = 0; i < len; i++)
    {
        result = max(b[i], result);
    }
    cout << result << endl;
    delete []b;
    system("pause");
} 
void getBiggestMultipleResults(int *pArray, int *pResultsArray, int len)
{
    pResultsArray[0] = 1;
    //前累乘
    for (int i = 1; i < len; i++)
    {
        pResultsArray[i] = pResultsArray[i - 1] * pArray[i - 1];
    }
    //后累乘
    for (int i = len - 2; i > 0; i--)
    {
        //把pResultsArray[0]作为临时变量
        pResultsArray[0] *= pArray[i + 1];
        pResultsArray[i] *= pResultsArray[0];
    }
    pResultsArray[0] *= pArray[1];
}看完这个数学分析的方法,不禁感叹,还是要数学好啊!不信,你看吧。
假设N个证书的乘积为P, 针对P的正负性进行分析:
那么,数组中至少有1个0。除去这个0之外,其他N-1个数的乘积为Q,根据Q的正负性进行分析:
结果返回0.  
分析:Q=0说明Q数组里面还有1个0,加上前面P数组中的1个0,我们可以断定,任意N-1个数的乘积只能为0,那么结果返回0.
结果返回Q 
分析:如果以0替换此时Q数组里面任意一个数的话,那么结果都是0,所以最后结果为Q
结果返回0 
分析:如果以0替换此时Q数组里面的任意一个,则结果为0,0比负数大,所以结果返回0
从数组中去除一个绝对值最小的正整数,这样得到剩下的
从数组中去除一个绝对值最小的负整数,剩下的
这样的解法,你服不服?反正我服了。
还有一些题目没有时间更新了,因为要汇报啊。预告一下,这个还会有更新那个什么,“最大递增子序列”,“最大的group个数”,后续会更新上来。今天就到这儿位置喽,祝大家晚上愉快,good night哦。(~ o ~)~zZ
原文:http://blog.csdn.net/xiaxia__/article/details/44985509