题目描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
- 动态规划版本,dp[i] 以nums[i]结尾的最长上升子序列的长度,目标是找到最大的dp[i]
- 对于nums[i],需要遍历dp数组,如果nums[i]大于dp[k],则dp[i]的一个可能的值就是dp[k]+1
- 贪心+二分查找法
- d[i]表示长度为i+1的序列的最后一位数值.
- d[i]单调递增,假如存在i < j, d[i] > d[j], 那么把d[j]的后k(j - i)位删掉,则此时d[k]小于d[i],但其实k=i,所以矛盾,得证d[i]单调递增。
- 贪心之处在于希望长度是i的字符串的最后一位尽可能的小,这样就可以更容易的拼出长一点的上升子序列。
- 在d[i]单调递增的情况下。
- 如果当前元素比d的最末尾的一个元素都大,那么当前元素添加到d数组的末尾。
- 如果当前元素小于d最末尾的一个元素,那么说明当前元素肯定可以替换d数组中的第k位,使得d[k]变小。
-上述问题转变为如何在d中找到index,使得d[index - 1] < nums[i],d[index] > nums[i],这样就可以用nums[i]替换原d[index]的值,使得长度是index+1的上升子序列的最后一位变小。
代码
class Solution {
public int lengthOfLIS(int[] nums) {
/** 动态规划版本
* dp[i] 以nums[i]结尾的最长上升子序列的长度
*/
int[] dp = new int[nums.length];
dp[0] = 1;
int maxRes = 1;
for(int i = 1; i < nums.length; i++){
int maxVal = 1;
for(int j = 0; j < i; j++){
if(nums[i] > nums[j]){
maxVal = Math.max(maxVal, dp[j]+1);
}
}
dp[i] = maxVal;
maxRes = Math.max(maxRes, dp[i]);
}
return maxRes;
/**
* 二分 + 贪心
* d[i]表示长度为i+1的序列的最后一位数值
*/
int[] d = new int[nums.length];
d[0] = nums[0];
int left = 0, right = 0;
for(int i = 1; i < nums.length; i++){
if(nums[i] > d[right]){
d[++right] = nums[i];
} else {
int rightTemp = right;
while(left < right){
int mid = (left + right) / 2;
if(nums[i] > d[mid]){
right = mid;
} else if(nums[i] < d[mid]){
left = mid + 1;
} else {
right = mid;
break;
}
}
d[right] = nums[i];
right = rightTemp;
left = 0;
}
}
return right + 1;
}
}