最大子数组之和、最大子数组之积、最长递增子序列求法

一、昨天做爱奇艺笔试题,最后一道编程题是求整型数组最长递增子序列,由于时间关系,没有完全写出来,今天重新来做做这一系列题。
《1》 最大子数组之和
首先从最简单的最大子数组之和求取。数组里有正数、负数、零。设包含第 i 个元素的子数组的和为 Sum,则Sum的值为
Sum(i) = Sum(i-1) + arrey[i]; 显然如果arrey[i]<=0,则Sum(i)<=Sum(i-1);则必须把Sum(i)=arrey[i];同时maxSum用来保存Sum最大值。时间复杂度为o(n);

#include<iostream>
#include<math.h>
using namespace std;
int  max(int a,int b){
    return a>b?a:b;
}
int FindGreatestSumOfSubArrey(int input[],int length){
    int TempSum=input[0];
    int MaxSum=TempSum;
    for (int i = 1;i < length; i++){
        TempSum = max(TempSum + input[i],input[i]);
        MaxSum = max(TempSum,MaxSum);
    }
    return MaxSum;
}

int main(){
    int input[] = {1,-2,-5,3,5};
    cout<<FindGreatestSumOfSubArrey(input,5)<<endl;// 结果为8
    return 0;
}

上面题目中,如果要求返回和最大子数组,则可以设置两个指针begin,end来指向maxSum的第一个元素和最后一个元素。
设置指针first,last来指向当前Sum的第一个元素和最后一个元素。当Sum>=maxSum时,如果begin=first时,通过last来更新end,如果begin!=first,则设置begin=first,end=last.
当Sum

#include<iostream>
#include<math.h>
using namespace std;
int  max(int a,int b){
    return a>b?a:b;
}
void FindGreatestSumOfSubArrey(int input[],int length){
    int TempSum=input[0];
    int MaxSum=TempSum;
    int first,last;
    int begin,end;
    first = last = begin = end = 0;
    for (int i = 1;i < length; i++){
        TempSum += input[i];
        if (TempSum <= input[i]){
            first = last = i;
            TempSum = input[i];
        }
        else
            last = i;
        if (MaxSum <= TempSum){
            MaxSum = TempSum;
            begin = first;
            end = last;
        }

    }
    cout<<"MaxSum = "<<MaxSum<<endl;
    for (i = begin; i <= end; i++)
        cout<<input[i]<<" "; // 输出子数组
}

int main(){
    int test1[] = {1,-2,-5,3,5};
    int test2[]={1,1,1,1,1};
    int test3[]={-1,-1,-1,-1,-1};
    int test4[]={-1,2,-3,1,1};
    FindGreatestSumOfSubArrey(test1,5);
    cout<<endl;
    FindGreatestSumOfSubArrey(test2,5);
    cout<<endl;
    FindGreatestSumOfSubArrey(test3,5);
    cout<<endl;
    FindGreatestSumOfSubArrey(test4,5);
    cout<<endl;
    return 0;
}

对于第四组测试数据,其中有最大子数组之和有两个。一个{2},另一个是{1,1};上述代码得到的是后一种答案,有兴趣的可以把最大和相同的子数组全部输出。
《2》最大子数组之积
分别求取以第i个元素开始的数组的积,然后在求取所有积中最大值即可。时间复杂度为o(n^2)

    #include <stdio.h>
    int getmaxsub(int *input, int size){
        if(input == NULL || size == 0) return 0xFFFF;
        if(size == 1) return input[0];
        int current_product;
        int max = input[0];
        int first,last;
        first = last = 0;
        for(int len = 0;len<size;len++){
            current_product =input[len];
            for(int end = len + 1; end < size; end++){
                current_product *= input[end];
                if(current_product > max){
                    first = len ;
                    last = end;
                    max = current_product;  
                }
            }
        }
        for (int k = first; k <= last ;k++)
            printf("%d ",input[k]);
        printf("\n");
        return max;
    }

    int main(){
        int input[] = {5,1,-2,4,9,1};
        printf("maxmult : %d \n", getmaxsub(input,6));
        return 0;
    }

动态规划做法,假设数组为a[N],max[N] 表示以下标为 i 结尾的子数组乘积最大值,min[N] 表示以下标为 i 结尾的子数组乘积最小值。为了处理数组元素为负的问题,必须将最小乘积也保存起来。很容易想到,若当前元素a[i]为负数,那么a[i]*max[i-1]得到的值并不一定比a[i]*min[i-1]大,因为min[i-1]可能为负,如果min[i-1]的绝对值大于max[i-1],那么a[i]*min[i-1]负负相乘的值是更大的,因此有转移方程:

max[i] = MaxinThree(a[i], a[i]*max[i-1], a[i]*min[i-1]); //求三者最大
min[ i] = MininThree(a[i], a[i]*max[i-1], a[i]*min[i-1]); //求三者最小

#include<stdio.h>
#include<iostream>
using namespace std;
int MaxinThree(int a, int b, int c)  
    {  
        return (((a>b)?a:b)>c) ? (a>b?a:b) : c;  
    }  
int MininThree(int a, int b, int c)  
    {  
        return (((a<b)?a:b)<c) ? (a<b?a:b) : c;  
    }  
void FindGreatestMultiOfSubArrey(int *input,int size){
    int *max = new int[size];
    int *min = new int[size];
    int product;
    max[0] = min [0] = input[0];
    product = max[0];
    for (int i = 1; i < size; i++){
        max[i] = MaxinThree(input[i],input[i]*max[i-1],input[i]*min[i-1]);
        min[i] = MininThree(input[i],input[i]*min[i-1],input[i]*max[i-1]);
        if(max[i] > product)
            product = max[i];
    }
    cout<<product<<endl;
}
int main(){
    int input[] = {5,-1,-2,4,9,1};
    void FindGreatestMultiOfSubArrey(int *input,int size);
    FindGreatestMultiOfSubArrey(input,6);
    return 0;

}

《3》最长递增子序列
最长递增序列不要求数组元素连续问题,返回递增序列长度和递增序列。o(n^2)做法,顺序比较以第i个元素开头的递增序列即可。

#include<stdio.h>
#include<iostream>
using namespace std;
void FindGreatestAddOfSubArrey(int *input,int size){
    int *result = new int[size];
    int *pre = new int[size];
    int k,MaxLen = 0;
    for (int len = 0; len < size; len++){
         int temp = input[len];
         int cnt = 0;
         pre[0] = input[len];
         for(int end = len + 1; end < size; end++){
            if (input[end] > temp){
                temp = input[end];
                pre[++cnt] = temp;
            }
        }
        if (cnt >= MaxLen){
            k = 0;
            MaxLen = cnt;
            while(k <= cnt){
                result[k] = pre[k];
                k++;
            }
        }
    }
    cout<<MaxLen+1<<endl;
    for(int i = 0;i < k; i++)
        cout<<result[i]<<" ";
    cout<<endl;
}

int main(){
    int test1[] = {5,-1,-2,4,9,1};
    int test2[] = {1,2,3,4,5,6};
    int test3[] = {6,5,4,3,2,1};
    FindGreatestAddOfSubArrey(test1,6);
    FindGreatestAddOfSubArrey(test2,6);
    FindGreatestAddOfSubArrey(test3,6);
    return 0;
}

利用动态规划来做,假设数组为1, -1, 2, -3, 4, -5, 6, -7。我们定义LIS[N]数组,其中LIS[i]用来表示以array[i]为最后一个元素的最长递增子序列。
使用i来表示当前遍历的位置:
当i = 0 时,显然,最长的递增序列为(1),则序列长度为1。则LIS[0] = 1
当i = 1 时,由于-1 < 1,因此,必须丢弃第一个值,然后重新建立序列。当前的递增子序列为(-1),长度为1。则LIS[1] = 1
当i = 2 时,由于2 > 1,2 > -1。因此,最长的递增子序列为(1, 2),(-1, 2),长度为2。则LIS[2] = 2。
当i = 3 时,由于-3 < 1, -1, 2。因此,必须丢掉前面的元素,重建建立序列。当前的递增子序列为(-3),长度为1。则LIS[3] = 1。
依次类推之后,可以得出如下结论。
LIS[i] = max{1, LIS[k] + 1}, array[i] >array[k], for any k < i
最后,我们取max{Lis[i]}。

#include<stdio.h>
#include<iostream>
using namespace std;
void FindLongestAscSequence(int *input,int size){
    int *list = new int[size];// 用来存储以第i个元素结尾的最长递增子序列
    int MaxLen = 1;
    int k = 0;
    for (int i = 0; i < size; i++){
        list[i] = 1 ;
        for ( int j = 0; j < i; j++){
            if ((input[i] > input[j]) && (list[j] +1 > list[i]) )
                   list[i] = list[j] + 1;
        }
        if (MaxLen < list[i]){
            MaxLen = list[i];
        }
    }
    cout<<MaxLen<<endl;
}

int main(){
    int test1[] = {5,-1,-2,4,9,1};
    int test2[] = {1,2,3,4,5,6};
    int test3[] = {6,5,4,3,2,1};
    FindLongestAscSequence(test1,6);
    FindLongestAscSequence(test2,6);
    FindLongestAscSequence(test3,6);
    return 0;
}


后续继续更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章