最大子數組之和、最大子數組之積、最長遞增子序列求法

一、昨天做愛奇藝筆試題,最後一道編程題是求整型數組最長遞增子序列,由於時間關係,沒有完全寫出來,今天重新來做做這一系列題。
《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;
}


後續繼續更新。

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