Longest Increasing Subsequence

Given an unsorted array of integers, find the length of longest increasing subsequence.

For example,
Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.

Your algorithm should run in O(n2) complexity.

Follow up: Could you improve it to O(n log n) time complexity?

问题分析:

根据问题抽象出最优子结构。设定原数组为S[n],以元素S[k]为结尾的最长递增子序列长度为list[k].则list[n + 1] = max(list[k] + 1), 其中k = 0, 1, ..., n, 且s[n + 1] > s[k].即在寻找以S[n + 1]为结尾的最长递增子序列长度时,都需要找出原数组中在S[n + 1]前面小于S[n + 1]的元素。

利用简单的dp做法时间复杂度为O(n^2),空间复杂度为O(n).代码如下:

public class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) return 0;
        int[] rst = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            rst[i] = 1;
        }
        int result = 1;
        for (int i = 1; i < nums.length; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i] && rst[j] + 1 > rst[i]) {
                    rst[i] = rst[j] + 1;
                }
            }
            result = Math.max(result, rst[i]);
        }
        return result;
    }
}

 

问题优化:

同样采用动态规划的思想,可以使用一个数组dp[]用来记录以该下标加1为LIS个数的最后一个元素的最小值。可以发现使用数组dp[0...MAXLENGTH - 1]其中dp[i]表示长度为i + 1的LIS的最小末尾元素对于dp[0...MAXLENGTH - 1]中值合法对应的最大下标加1,就是当前最长的LIS,也即利用dp[]更新自身。这种情况下,对于每次新添加的元素,只需要在dp[]数组中寻找该元素应该插入的位置即可。这一步可以利用Arrays.binarySearch来完成,从而将整体的时间复杂度由O(n^2)下降到O(n log n) 。

从下面Arrays.binarySearch的javadoc可以看到:

1)找到key元素,返回key元素下标;

2)没找到key元素,返回(-(insertion point) - 1).其中insertion point为第一个比key元素大的元素的index,如果都比key小,则为toIndex。

public static int binarySearch(int[] a,
               int fromIndex,
               int toIndex,
               int key)
Searches a range of the specified array of ints for the specified value using the binary search algorithm. The range must be sorted (as by the sort(int[], int, int) method) prior to making this call. If it is not sorted, the results are undefined. If the range contains multiple elements with the specified value, there is no guarantee which one will be found.
Parameters:
a - the array to be searched
fromIndex - the index of the first element (inclusive) to be searched
toIndex - the index of the last element (exclusive) to be searched
key - the value to be searched for
Returns:
index of the search key, if it is contained in the array within the specified range; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the array: the index of the first element in the range greater than the key, or toIndex if all elements in the range are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found.
Throws:
IllegalArgumentException - if fromIndex > toIndex
ArrayIndexOutOfBoundsException - if fromIndex < 0 or toIndex > a.length
Since:
1.6

利用这两点,当该数组元素没找到key元素时,将insertion point处的元素替换为key元素,且如果key元素是在末尾新加时,将结果加1.

代码如下:

public class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int[] dp = new int[nums.length];
        int len = 0;
        for (int i = 0; i < nums.length; i++) {
            int index = Arrays.binarySearch(dp, 0, len, nums[i]);
            if (index < 0) {
                index = - (index + 1);
            }
            dp[index] = nums[i];
            if (index == len) {
                len++;
            }
        }
        return len;
    }
}

值得注意的是,这个保存递增数列的数组最终存的长度为len的递增数列不一定就是实际的结果序列,虽然其长度与结果序列相同。

比如nums为[2,3,4,1],最后len为3,dp中的数列为[1,3,4]。

这个题目的解法确实很巧妙,有一些小细节值得仔细思考。

posted on 2016-05-19 18:25  ShinningWu  阅读(174)  评论(0编辑  收藏  举报

导航