求数组中最长递增子序列

例如在序列1,-1,2,-3,4,-5,6,-7中,其最长的递增子序列为1,2,4,6。

解法一:假设在目标数组array[]的前i个元素中,最长递增子序列的长度为LIS[i]。那么,LIS[i+1]=max{1,LIS[k]+1},array[i+1]>array[k],forany k<=i。(LIS[i]是指:假设在数组的前i个元素中,以array[i]为最大元素的最长递增子序列的长度)。

思想:穷举的方法,LIS中记录了长度,通过无后效性性质,只需与末尾的array[i]比较即可(很好理解),复杂度为O(N*N+N)=O(N)。

int LIS(int[] array){

    int[] LIS =new int[array.length];

    for(int i =0;i

        LIS[i] =1;

        for(int j= 0;j

            if(array[i]>array[j]&&LIS[j]+1>LIS[i]){//穷举比较,获得最长长度

                LIS[i]= LIS[j]+1;}

        }

    }

    returnMax(LIS);

}

解法二:在递增序列中,如果i,那么就会有MaxV[i]MaxV[j]这情况。

maxV[i]表示长度为i的递增子序列的最大元素的最小值;nMaxLIS表示数组最长递增子序列长度

代码:

int LIS(int[] array){

    int[] maxV =new int[array.length+1];//记录数组中的递增序列信息

    int[] LIS =new int[array.length];

    maxV[1] =array[0];

    maxV[0] =min(array)-1;//数组中最小值-1,边界值。

    for(int i =0;i

        LIS[i] =1;

    }

    int nMaxLIS =1;

    for(int i = 1;i

        int j;

//先考虑当前最大长度子序列,看可否增加,不行再长度递减观察

        for(j =nMaxLIS;j>=0;j--){

            if(array[i]>maxV[j]){

                LIS[i]= j+1;

                break;

            }

        }

//如果当前最长序列大于最长递增序列长度,更新最长信息

        if(LIS[i]>nMaxLIS){

            nMaxLIS= LIS[i];

            maxV[LIS[i]]= array[i];

        }

//如果array[i]比最长递增子序列最大值要小,则替换原先的最大值(前面break处就有array[i]>maxV[j],添maxV[j]

        elseif(maxV[j]

            maxV[j+1]= array[i];

        }

    }

    returnnMaxLIS;

}

时间复杂度为O(N*N)。内部循环穷举查询那边,可以用二分搜索加速,把时间复杂度降为O(N*log2N)。

附加:最长公共上升子序列

f[j]为必选择b[j]为末尾时的最长公共上升子序列

代码如下:

//通过固定a[i],循环b[j]进行比较。很好理解,忘记了,可以结合1,2,0,4,5和1,0,4,5,2的例子跟代码一起理解。

int i,j,k;

for(i=0;i

    k = 0;

    for(j =0;j

        if(a[i]== b[j])

            if(f[j]

                    f[j]= f[k]+1;

        //必须要有,很好理解当a[i]值比b[j]大时,需要更新k,因为能够去与f[j]比较了,而a[i]比b[j]小时,是没资格去获取f[j]

        if(a[i]>b[j])

            if(f[k]

                k= j;

    }

}

返回max(f(i));

你可能感兴趣的:(编程之美)