[编程题]最长递增子序列

对于一个数字序列,请设计一个复杂度为O(nlogn)的算法,返回该序列的最长上升子序列的长度,这里的子序列定义为这样一个序列U1,U2…,其中Ui < Ui+1,且A[Ui] < A[Ui+1]。
给定一个数字序列A及序列的长度n,请返回最长上升子序列的长度。
测试样例:
[2,1,4,3,1,5,6],7
返回:4

package alex.suda.dp;

import java.util.Scanner;

public class test1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            int n = scanner.nextInt();
            int[] A = new int[n];
            for (int i = 0; i < n; i++) {
                A[i] = scanner.nextInt();
            }
            System.out.print(findLongest1(A, n));
        }
    }
    //方法1 o(n*n)
    public static int findLongest(int[] A, int n) {
        // 以[2,1,4,3,1,5,6]为例。使用i来表示当前遍历的位置:
        // i = 1 时,最长递增序列为{1} ,序列长度为1
        // i = 2时,1 < 2 所以得丢弃2 重新建立最长递增序列{1},序列长度为1
        // i = 3时,4>2,4>1 最长递增序列为{2,4} {1,4},长度为2
        // 以此类推,可得:d[i+1] = max{1,d[k]+1},其中对于所有的k<= i ,A[i+1] > A[k]
        // d[i] 表示A数组的前i个元素当中,最长递增序列的长度
        int[] d = new int[n];
        for (int i = 0; i < n; i++) {
            d[i] = 1; // 初始化默认长度
            for (int j = 0; j < i; j++) { // 前面最长的序列
                if (A[i] > A[j] && d[j] + 1 > d[i]) { // 增加了当前的数之后,大于原来的长度,就更新
                    d[i] = d[j] + 1;
                }
            }
        }
        int maxDis = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            if (d[i] > maxDis) {
                maxDis = d[i];
            }
        }
        return maxDis;
    }

    // 方法2:O(nlogn) 采用两个数组d[],maxV[]。其中d[i]表示必须以A[i]结尾的最长递增子序列的长度,
    // maxV[i]表示递增子序列长度为i+1的所有序列中结尾最小的值.d[0]=1,maxV[0]=A[0]
    // maxV[]是排好序的,所以在更新maxV[]时查找A[i]的位置时采用二分搜索
    public static int findLongest1(int[] A, int n) {
        int[] d = new int[n];
        int[] maxV = new int[n];
        d[0] = 1;
        maxV[0] = A[0];
        int maxDis = 0;
        int count = 0;
        for (int i = 1; i < n; i++) {
            // 二分法查找A[i]在maxV[]序列中的所处的位置。
            int right = count;
            int left = 0;
            int mid;
            while (left <= right) {
                mid = (right + left) / 2;
                if (A[i] >= maxV[mid]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
            // 更新maxV[]的值
            // ,表示递增子序列长度为left+1的所有序列中结尾最小的值变成了A[i](因为A[i]
            maxV[left] = A[i];
            if (left > count) {
                count = left;
            }
            d[i] = left + 1;
            if (d[i] > maxDis) {
                maxDis = d[i];
            }
        }
        return maxDis;
    }
}

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