Given an unsorted array of integers, find the length of longest increasing subsequence.
For example,
Given
The longest increasing subsequence is
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?
Solution:
Method 1: Dynamic Programming
We use LIS[I] to denote the max LIS that end at nums[i] including nums[i].
It is the max of all (LIS[j] + 1), where j < i, if nums[i] > nums[j].
The time complexity is O(n2) and the space complexity is O(n).
Code:
public class Solution { public int lengthOfLIS(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int max = 1; int[] LIS = new int[nums.length]; for (int i = 0; i < nums.length; i++) { LIS[i] = 1; for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { LIS[i] = Math.max(LIS[i], LIS[j] + 1); } } max = Math.max(max, LIS[i]); } return max; } }
Method 2: DP + Binary Search
The idea is to maintain an array that array[i] means the smallest number that can form an increasing sequence with length i.
For example, [4, 5, 6, 3]
len = 1 : [4], [5], [6], [3] => tails[1] = 3
len = 2 : [4, 5], [5, 6] => tails[2] = 5
len = 3 : [4, 5, 6] => tails[3] = 6
Therefore, we create an array tails with length + 1.
Traverse all numbers from the input array.
For each number num, use binary search to find the first index in tails where tails[i] >= num.
Replace tails[i] with num.
After traversing, go backward and find the first valid index, which is the longest length.
The time complexity is O(nlogn) and the space complexity is O(n).
Code:
public class Solution { public int lengthOfLIS(int[] nums) { int[] tails = new int[nums.length + 1]; Arrays.fill(tails, Integer.MAX_VALUE); tails[0] = Integer.MIN_VALUE; int max = 0; // input can be []. for (int num : nums) { int index = findFirst(tails, num); tails[index] = num; max = Math.max(max, index); } return max; } public int findFirst(int[] nums, int target) { int start = 1; int end = nums.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (nums[mid] < target) { start = mid; } else { end = mid; } } if (nums[start] >= target) { return start; } return end; } }