更正 最长递增子序列 算法复杂度,添加O(nlogn)时间复杂度算法

This commit is contained in:
liyao
2021-08-14 16:24:20 +01:00
parent 4028047d9d
commit abb9cfd5be

View File

@ -33,7 +33,7 @@
* 1 <= nums.length <= 2500 * 1 <= nums.length <= 2500
* -10^4 <= nums[i] <= 104 * -10^4 <= nums[i] <= 104
## 方法一 动态规划
## 思路 ## 思路
最长上升子序列是动规的经典题目这里dp[i]是可以根据dp[j] j < i推导出来的那么依然用动规五部曲来分析详细一波 最长上升子序列是动规的经典题目这里dp[i]是可以根据dp[j] j < i推导出来的那么依然用动规五部曲来分析详细一波
@ -190,10 +190,130 @@ const lengthOfLIS = (nums) => {
}; };
``` ```
*复杂度分析* *复杂度分析*
- 时间复杂度O(nlogn)。数组 nums 的长度为 n我们依次用数组中的元素去更新 dp 数组,相当于插入最后递增的元素,而更新 dp 数组时需要进行 O(logn) 的二分搜索,所以总时间复杂度为 O(nlogn)。 - 时间复杂度O(n^2)。数组 nums 的长度为 n我们依次用数组中的元素去遍历 dp 数组,而遍历 dp 数组时需要进行 O(n) 次搜索,所以总时间复杂度为 O(n^2)。
- 空间复杂度O(n),需要额外使用长度为 n 的 dp 数组。 - 空间复杂度O(n),需要额外使用长度为 n 的 dp 数组。
## 方法二 贪心策略+二分搜索
使用贪心策略和二分搜索可以进一步将算法时间复杂度将为O(nlogn)。
## 思路
为了使得到的子序列尽可能长,我们需要使序列上升得尽可能慢。
对于长度为n的数组 nums我们从0到n-1依次遍历数组中的每个元素nums[i],更新在0到i范围内最长上升子序列的长度len以及 在0到i范围内上升子序列的长度为1到len时对应长度子序列最右端的最小值将结果保存在list中。实际编码过程中list长度即为len。
## 可行性
当我们遍历完数组nums中第n-1个元素时list中保存的是0到n-1范围内最长上升子序列的长度即为所求。
## 算法复杂度分析
1. list中的元素是单调递增的。可以用反证法来证明假设对于0<=i<j<len有list[i]>=list[j]那么我们可以在list[j]对应的子序列中删除最后j-i个元素得到长度与list[i]相同的子序列其最右端的值max<list[j]<=list[i],与list的定义矛盾
2. 假设我们已经得到0到i-1范围内对应的list,我们可以在O(logn)的时间复杂度内更新list,得到0到i范围内的list
1. if(nums[i]>list[len-1],此时list中子序列长度为1到len的对应的最右端最小值不变并新增长度为len+1的子序列最右端的最小值为nums[i],时间复杂度O(1);
2. if(nums[i]<=list[len-1])此时我们可以在0到len-1范围内找到k,list[k]为>=nums[i]的最小值,由于list单调递增所以我们可以使用二分搜索在O(logn)的时间复杂度内找到k。
1. 对于0<=j<k,list[j]<nums[i]恒成立对应list[j]的值不需要更新
2. 对于list[k]其值更新为nums[i],因为原本list[k]对应的子序列的倒数第二项的值可以=list[k-1]<nums[i]。
3. 对于k<j<=len-1,对应的list[j]不需要更新因为这些list[j]对应的子序列的倒数第二项的值>nums[i];
3. 综上算法时间复杂度为O(nlogn),空间复杂度为O(n),需要O(n)的空间保存list。
代码如下
Java
```java
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
if(n==0){return 0;}
List<Integer> list=new ArrayList<>();
list.add(nums[0]);
for (int i = 1; i < n; ++i) {
if (nums[i] > list.get(list.size()-1)) {
list.add(nums[i]);
} else {
int k=binarySearch(list,nums[i]);
list.set(k,nums[i]);
}
}
return list.size();
}
int binarySearch(List<Integer>list, int num){
int len=list.size();
int l=0,r=len-1,ans=len-1;
while(l<=r){
int mid=l+(r-l)/2;
if(list.get(mid)<num){
l=mid+1;
}else{
r=mid-1;
ans=mid;
}
}
return ans;
}
}
```
实际运行过程中list的长度不会超过n所以我们可以用数组来模拟list代码如下。
Java
```java
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
if(n==0){return 0;}
//初始化listlen记录list长度
int[] list=new int[n];
int len=0;
//添加元素到list并更新len的值
list[len++]=nums[0];
for (int i = 1; i < n; ++i) {
if (nums[i] > list[len-1]) {
list[len++]=nums[i];
} else {
int k=binarySearch(list,len,nums[i]);
list[k]=nums[i];
}
}
return len;
}
int binarySearch(int[] list,int len, int num){
int l=0,r=len-1,ans=len-1;
while(l<=r){
int mid=l+(r-l)/2;
if(list[mid]<num){
l=mid+1;
}else{
r=mid-1;
ans=mid;
}
}
return ans;
}
}
```
----------------------- -----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321) * B站视频[代码随想录](https://space.bilibili.com/525438321)