找出數組中長度最長的等差數列

T1:
http://blog.chinaunix.net/uid-26456800-id-3447973.html

求出一個有序數組中公差爲d的最長等差數列。
 
較簡單動態規劃:
設等差數列的起始下標爲s,當前下標爲i,從s到i,構成的最長的等差數列長度爲f(s,i),有
  f(s,i+1) = f(s,i) +1 ( input[i+1] == input[s] + f(s,i)*d)
             f(s,i)    ( input[i+1] != input[s] + f(s,i)*d)
 
最長的數列長度爲:
    maxLen = max{f(s,i) s∈[1 ..  max-min] i∈[0,sizeof(input)) }
 
算法時間複雜度應爲n的平方,空間複雜度可優化至O(1) (代碼中爲O(n))
 
http://blog.csdn.net/cwqbuptcwqbupt/article/details/7546674代碼如下:
    #include <stdio.h>
    #include <stdlib.h>
    #define MAX 1000

    void GetSeq(int *input, int size, int d){
        if(input == NULL) return;
        int result[MAX] = {0};
        
        int startIndex = 0;
        int maxLen = 0;
        int maxLenStartIndex = -1;
        for(;startIndex < size-maxLen; startIndex++){
            int i = startIndex;
            result[i] = 1;
            i++;
            for(;i<size;i++){
                result[i] = result[i-1];
                if(input[i] == input[startIndex] +result[i-1]*d){
                    result[i]++;
                    if(result[i]> maxLen){
                        maxLen = result[i];
                        maxLenStartIndex = startIndex;
                    }
                }
                else if(input[i] > input[startIndex] + result[i-1]*d)
                    break;
            }
            }
        printf("Max sequence start index: %d, length: %d \n", maxLenStartIndex, maxLen);
    }

    int main(){
        int input[] = {1,3,4,6,7,9,10,12,13,15,16,18,21,24};
        GetSeq(input, sizeof(input)/sizeof(int), 3);
    }

T2:

求微軟面試題:求整數隨機數構成的數組中找到長度大於=3的最長的等差數列

輸出等差數列由小到大: 

如果沒有符合條件的就輸出[0,0]

格式:

輸入[1,3,0,5,-1,6]

輸出[-1,1,3,5]

要求時間複雜度,空間複雜度儘量小

網上有動態規劃的解法,時間複雜度O(N^3):http://openuc.sinaapp.com/?p=286

而本文要提另一種動態規劃解法,時間複雜度O(N^2),空間複雜度比較大,一會說。

方法:

第一步都一樣,先排序。

第二步,動態規劃。在一個已排好序的數列中,等差數列的差d滿足 0 <= d <= a[N-1] - a[0]。於是,設一個二維表dp,有a[N-1] - a[0] + 1 行,N列,dp[i][j]記錄數列a[j],公差爲i下的最長數列長度。那麼很明顯有:dp[i][j] = dp[i][  index_of( a[j] + i )  ] + 1。其中index_of(num)表示數num在數組a中的索引。上述dp的意思是:如果a[j]能構成等差數列的一份子,公差爲i,那麼它的下一項就是a[j] + i,這當然要求a[j] + i存在於數組a中啦~而且,a[j]構成的數列的長度就是由 a[j] + i 構成數列長度加1. 依據上述分析,只要對數組a由尾到頭遍歷,對每個a[j],求出所有公差從0到a[N-1]-a[0]下的最長數列長度,則問題就得解了。

注意幾個問題:

1. 上述分析過程中要求求出所有公差從0到a[N-1]-a[0],但實際上並不需要這麼一個一個的求,因爲以任何a[j],它能構成等差數列,則公差一定是 a[  k ] - a[ j ],這裏 j < k < N,因此,求解的範圍得到縮小,因此整體的時間複雜度爲0(N^2)。

2. 另一個實現問題是,dp只記錄了最長數列的長度,而我們爲了能回朔並輸出等差數列,我們還需要知道構成最長等差數列a[j]的下一個數是什麼,因此,需要同時記錄下一跳的索引。在代碼中,我用pair<int,int>來記錄,first記錄長度,second記錄下一跳索引。

3. 注意處理a[j]與多個數差值相同的情況,比如 1,3,3,對a[0]=1,它和a[1],a[2]的差值相同,所以對於a[0],公差爲2而言,即dp[2][0],它只需要更新一次即可。
    #include "stdafx.h"  
      
    #include <stdio.h>  
    #include <iostream>  
    using namespace std;  
      
    const int N = 10;  
    const int INVALID_IDX = -1;  
      
    void show(int* a,int n)  
    {  
        for (int i=0;i<n;++i)  
        {  
            cout<<a[i]<<",";  
        }  
        cout<<endl;  
    }  
      
    inline int compare(const void* p1,const void* p2)  
    {  
        return *(int*)p1 - *(int*)p2;  
    }  
      
    void longest_seq(int* a)  
    {  
        qsort(a,N,sizeof(int),&compare);  
      
        int R = a[N-1]-a[0]+1;  
        pair<int,int>** dp = new pair<int,int>*[R];  
        for (int i=0;i<R;++i)  
        {  
            pair<int,int>* row = new pair<int,int>[N];   
            for (int j=0;j<N;++j)  
            {  
                row[j].first = 0;       //記錄當前最長數列的長度  
                row[j].second = INVALID_IDX;//記錄與first相對應的等差數列的下一值在數組a中的索引  
            }  
            dp[i] = row;  
        }  
          
        int maxlen = 0;  
        int rowidx = INVALID_IDX;  
        int colidx = INVALID_IDX;  
      
        for (int i=N-2;i>=0;--i)  
        {  
            for (int j=i+1;j<N;++j)  
            {  
                if (dp[ a[j]-a[i] ][i].first != 0) continue;    //以該“差”爲行號的值如果已經存在,就不需要再爲相同的差值更新  
                  
                dp[ a[j]-a[i] ][i].first = dp[ a[j]-a[i] ][j].first + 1;  
                dp[ a[j]-a[i] ][i].second = j;  
      
                if (dp[ a[j]-a[i] ][i].first > maxlen)  
                {  
                    maxlen = dp[ a[j]-a[i] ][i].first;  
                    rowidx = a[j]-a[i];  
                    colidx = i;  
                }  
            }  
        }  
      
        if( maxlen > 1 )  
        {  
            cout<<"The longest seq is:"<<endl;  
            while( colidx != INVALID_IDX )  
            {  
                cout<<a[colidx]<<",";  
                colidx = dp[rowidx][colidx].second;  
            }  
            cout<<endl;  
        }  
        else  
        {  
            cout<<"0,0"<<endl;  
        }  
      
        for (int i=0;i<R;++i)  
            delete []dp[i];  
      
        delete []dp;  
    }  
      
      
    int main(void)  
    {  
        int a[N] = {8, 8, 7, 4, 1, 3, 3, 1, 8, 4};  
        longest_seq(a);  
      
        return 0;  
    }  


發佈了9 篇原創文章 · 獲贊 32 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章