算法学习之寻找最长等差数列

  最长等差数列就是在一个数组中,组成等差数列的最长的那一个,首先我们对数组排序,然后我们一般会先想到暴力法从第一个开始循环遍历整个数组,时间复杂度O(N^3),下面给出伪代码

               int i,j,k,len = 0
               for(i = 0; i < n; i++)
                     for(j = i+1; j < n; j++)
                    {
                        int delta = a[j] - a[i];
                        len = 2;
                        int tmp = a[j];
                        for(k = j+1; k < n; k++)
                       {
                            if(a[k] - tmp== delta)
                          {
                               len++;
                               tmp = a[k];
                          }
                       }
                      求出最大的len
                    }


第二种方法用动态规划,我们可以利用等差数列的性质来做,当等差数列长度为奇数的时候,比如相邻3个数,aiajak

则2*aj = ai+ak;只要任何一个关于它对称的两个数相加都等于它的2倍,有了这个性质我们就可以利用动态规划来做

dp[i][j] = d,表示以a[i]a[j]为前两个元素的等差数列长度为d,我们再来推状态转移方程,

 如果ai+ak<2*aj,说明ak - aj < aj - ai dp[i][j]的值不能确定,k接着往后走

如果ai+ak>2*aj,说明ak-aj > aj -ai,此时dp[i][j]的值为2,因为k不能往后走了,再往后走二者差距更大,所以a[i]a[j]开头的等差数列为2,然后再i--,开始寻找下个ai aj开头的等差数列

如果ai+ak==2*aj,则说明这三个数构成等差数列,此时dp[i][j]=dp[j][k]+1;也就是说以ajak开头的等差数列前加上了一个ai,状态转移确定好后,下面开始写代码

#include 
#include 
#include 
struct result
{
    int max_len;
    int i;
    int j;
};
int max(int a,int b)
{
    return a > b ? a : b;
}
void find_llap(int *arry,int n)
{
    int dp[50][50]; //dp[i][j]代表以arry[i]和arry[j]为头两个元素的等差数列,此题规模小,规模大的时候i,j可以做hash,就可以节省空间
    int i,j,k;
    struct result result;
    memset(dp,0,sizeof(dp));
    for(i = 0; i < n; i++)
    {
       dp[i][n] = 2; //因为以arry[n]为第二个元素的等差数列长度一定为2
    }
    if(n < 2)
    {
        for(i = 0; i < 2; i++)
            printf("%d ",arry[i]);
        return;
    }
    result.max_len = 0;
    for(j = n-1; j >= 1; j--)
    {
        i = j-1;k = j+1;
        while(i >= 0 && k <= n)
        {
            if(arry[i]+arry[k] > arry[j]<<1) //如果和ai+ak>2aj,说明ak-aj>aj-ai,这个构造不成等差数列,则k以后的更不行,因为数组是升序
             {
                 dp[i][j] = 2;                //所以此步骤直接赋值为2,及只有ai aj构造成等差数列
                 i--;
             }
            else if(arry[i]+arry[k] < arry[j]<<1)
            {
                k++;       //如果ai+ak<2aj,说明ak-aj= 0) /*这个循环是因为k已经到最后了,所以以这个j为第二个元素的所有数列都是2*/
         {
             dp[i][j] = 2;
             i--;
         }
     }
     int delta = arry[result.j] - arry[result.i];
     int last  = arry[result.i]+(result.max_len-1)*delta;
     for(i = arry[result.i]; i <= last; i+=delta)
        printf("%d ",i);

}

int main()
{
    int n;
    int arry[100];
    scanf("%d",&n);
    int i;
    for(i = 0; i < n; i++)
      scanf("%d",&arry[i]);
    find_llap(arry,n-1);
    return 0;
}


  依据这个性质,还有一种动态规划,dp[i][j]代表等差数列最后两个元素,然后从前往后遍历,得到的结果一样

      


              

你可能感兴趣的:(数据结构与算法)