这里是题目描述:LeetCode-300.最长上升子序列
动态规划方法 时间复杂度O(n2)
我们首先使用时间复杂度O(N2)的动态规划方法
建立一个和给定数组nums相同长度的一维表dp,dp[i]存储以nums[i]为末尾且包含nums[i]的最长子序列长度。初始化dp中所有值为1,从i=1开始遍历nums[i]并更新dp[i]
dp[i]更新方法:进入第二层循环,令j从0开始直到i-1,遍历dp[j];若dp[i]>dp[j],则由nums[i]为末尾元素,以nums[j]为倒数第二个元素的上升子序列长度可能是dp[I]的值,也就是说,取dp[j]+1和当前dp[i]两者中的较大值作为dp[i]
题解代码:
class Solution {
//时间复杂度为O(n^2)的动规解法
public int lengthOfLIS(int[] nums)
{
if(nums.length<=1)
{
return nums.length;
}
int[] dp=new int[nums.length]; //用于动规的一维表,dp[i]存储以nums[i]为末尾且包含nums[i]的最长子序列长度
dp[0]=1;
int maxLen=1;
for(int i=1;i<dp.length;i++)
{
int temp=1;
for(int j=0;j<i;j++)
{
if(nums[j]<nums[i])
{
temp=Math.max(temp,dp[j]+1);
}
}
dp[i]=temp;
maxLen=Math.max(maxLen,temp);
}
return maxLen;
}
}
时间复杂度:因为进行两层循环,所以是O(n2)
空间复杂度:建立一个一维动规表,所以是O(n)
非常规线性动态规划+二分查找 时间复杂度O(nlogn)
这个方法依然需要第一层时间规模为n的外层循环来遍历nums,不同的是在内层循环中使用了二分查找来构建动规表,时间规模是logn,提高了时间效率
通过重新设计状态定义,使整个 dp 为一个排序列表;这样在计算每个 dp[k] 时,就可以通过二分法遍历 [0,k) 区间元素,将此部分复杂度由 O(n) 降至 O(logn)
- 考虑维护一个列表 tails其中每个元素 tails[k] 的值代表长度为 k+1 的子序列尾部元素的值。
- 如 [1,4,6] 序列,长度为 1,2,3 的子序列尾部元素值分别为 tails=[1,4,6]
- 最后返回tail存储的能达到的最大长度
更具体的实现方法请参考:最长上升子序列(动态规划 + 二分查找,清晰图解)
题解代码:
class Solution {
public int lengthOfLIS(int[] nums)
{
if(nums.length<=0)
{
return nums.length;
}
int[] tail=new int[nums.length]; //用于动规的一维表,tail[i]存储截止当前遍历过的数组中,长度为i+1的上升子序列的尾部元素的最小值
tail[0]=nums[0];
int lastIndex=0;
for(int i=1;i<nums.length;i++)
{
// System.out.println("i: "+i);
// System.out.println(Arrays.toString(tail));
int left=0,right=lastIndex;
//int mid;
while(left<=right) //用二分法查找在tail[0:lastIndex]中nums[i]的大小分界点
{
int mid=(left+right)/2;
if(tail[mid]==nums[i])
{
break;
}
else if(tail[mid]<nums[i])
{
left=mid+1;
}
else
{
right=mid-1;
}
}
if(left>right) //若由tail[mid]==nums[i]跳出循环无需更新tail
{
//left指向tail中第一个大于nums[i]的值
if(left<=lastIndex) //tail[0:lastIndex]中有大于nums[i]的值,替换
{
//System.out.println("1: left: "+left+" lastIndex: "+lastIndex);
tail[left]=nums[i];
}
else //left>lastIndex,tail[0:lastIndex]中没有大于nums[i]的值,将nums[i]放在tail[lastIndex]处
{
//System.out.println("2: left: "+left+" lastIndex: "+lastIndex);
tail[lastIndex+1]=nums[i];
lastIndex+=1;
}
}
}
//System.out.println(Arrays.toString(tail));
return lastIndex+1;
}
}
时间复杂度:外层循环+内层的二分查找 O(nlogn)
空间复杂度:O(n)