问题描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
解题思路
动态规划: 需要O(n2)的时间复杂度
1、创建一个记录每个位置的最长子序列长度的数组dp
2、从左向右遍历数组,在每个位置查看前面的数字,找到前面小于当前数字的最大子序列
3、最后遍历一遍dp找出最长子序列长度
动态规划+二分查找:
1、创建一个数组dp记录最长子序列,用len记录子序列的长度
2、遍历数组,在dp数组中找当前数字,如果大于所有数字,将添加在当前dp后面
如果不大于所有数字,找到大于当前数字的第一个,替换它
3、最终在dp中的数字长度就是最长子序列的长度
代码
动态规划:
package solution;
class Solution {
public int lengthOfLIS(int[] nums) {
//记录每个位置的最大子序
int[] dp=new int[nums.length];
for(int i=0;i<nums.length;i++) {
dp[i]=1;
for(int j=i-1;j>=0;j--){
//如果前面的数小于当前数字
if(nums[j]<nums[i])
dp[i]=Math.max(dp[i],dp[j]+1);//找出前面的最大子序
}
}
int max=Integer.MIN_VALUE;
//遍历找出最大子序
for (int i:dp
) {
if(i>max){
max=i;
}
}
return max;
}
}
动态规划+二分查找
package solution;
class Solution {
public static void main(String[] args) {
Solution solution=new Solution();
solution.lengthOfLIS(new int[]{10,9,2,5,3,7,101,18});
}
public int lengthOfLIS(int[] nums) {
int[] dp=new int[nums.length];
int len=0;
for (int num:nums) {
//查找大于num的数字
int i = binarySearch(dp,0,len,num);
if(i<0){
i=-(i+1);
}
dp[i]=num;
if(i==len){
len++;
}
}
return len;
}
//二分查找
private int binarySearch(int[] dp,int start,int end,int num){
int low=start;
int high=end-1;
while (low<=high){
int mid=low+((high-low)>>>1);//相当于(high+low)/2,前者可以避免整数越界
int midVal=dp[mid];
if(midVal>num){
high=mid-1;
}else if(midVal<num){
low=mid+1;
}else {
return mid;
}
}
return -(low+1);
}
}