最长递增子序列

来源:互联网 发布:英雄传奇刷点卷软件 编辑:程序博客网 时间:2024/06/09 14:51

题目

求一个数组中最长递增子序列,要求时间复杂度尽量低。

  • 测试输入
    7
    2 1 4 3 1 5 6
  • 测试输出
    4

分析1

采取从后向前的分析思路。如果已知第i个元素存在于最长递增子序列中,那么前i个元素的最长递增子序列的问题可以转化为,在前i1个元素中所构成的最长递增子序列,加上这个第i个元素。由此可见子问题具有独立性,可以使用动态规划来完成。数组array[i]i=0n1,定义lis[i]表示以第i个元素结尾的最长递增子序列长度,则原问题为max(lis[i])i=0n1。要得到以array[i]结尾的最长递增子序列长度lis[i],就要在数组中找到array[k]k=0i1,要求array[k]<array[i]最大。有一种情况是array[k]都比array[i]大,那么以array[i]结尾的序列长度就是其自身长度1。递归式为

lis[i]=1,1,max(lis[k])+1,i=0array[k]array[i] for all k=0i1if array[k]<array[i] for k=0i1 
,原因在于max查找需要遍历一遍数组。如果能将查找过程优化就能得到更优的解法。应该注意一个事实,在找前i个元素的最长递增子序列时,应该尽量保证已经找到的子序列的末尾元素尽量小,这样第i+1个元素才更容易构成新的子序列。由此可以换一种定义方式,tail[len]表示长度为len的最长递增子序列的末尾元素,如果一个新来的元素x大于tail[len],则这个新元素x可以成为新的最长递增子序列的末尾元素,len增加1,否则就要用它替换tail[k]k=0len1满足tail[k]xtail[k1]<x,这意味着长度为k的最长递增子序列的末尾元素更新为xlen保持不变。用更小的x替换了tail[k],在不影响原有结果的情况下,更容易使新来的元素成为新子序列的末尾。另外,原问题中最长递增子序列长度就是len。对于数组[2 1 4 3 1 5 6]每一步分析如下:

i array[i] tail 0 2 2(初始值) 1 1 1(替换2) 2 4 1 4 3 3 1 3(替换4) 4 1 1 3 5 5 1 3 5 6 6 1 3 5 6

可以看到数组tail就保存着最长递增子序列的所有值,且其中的每个元素总是在可选元素的最后一个。因为数组tail一定递增,如果将查找过程改为使用二分查找,则整个算法的时间复杂度为O(nlog(n))

代码2

import java.util.Scanner;public class LIS {    static int solution2(int[] array, int[] tail) {        int n = array.length;        tail[0] = array[0];        int maxLen = 1;        for (int i = 1; i < n; i++) {            if (array[i] > tail[maxLen - 1]) {                tail[maxLen] = array[i];                ++maxLen;            } else {// 线性查找                for (int k = 0; k < maxLen; k++) {                    if (tail[k] >= array[i]) {                        tail[k] = array[i];                        break;                    }                }            }        }        return maxLen;    }    static int solution3(int[] array, int[] tail) {        int n = array.length;        tail[0] = array[0];        int maxLen = 1;        for (int i = 1; i < n; i++) {            if (array[i] > tail[maxLen - 1]) {                tail[maxLen] = array[i];                ++maxLen;            } else {// 二分查找                int pos = binarySearch(tail, maxLen, array[i]);                tail[pos] = array[i];            }        }        return maxLen;    }    static int binarySearch(int[] array, int limit, int value) {        int start = 0;        int end = limit - 1;        while (start <= end) {            int mid = start + (end - start) / 2;            if (array[mid] == value) {                return mid;            } else if (array[mid] < value) {                start = mid + 1;            } else {                end = mid - 1;            }        }        return start;    }    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        int n = sc.nextInt();        int[] array = new int[n];        for (int i = 0; i < n; i++) {            array[i] = sc.nextInt();        }        int[] tail = new int[n];        System.out.println(solution2(array, tail));           }}
0 0