mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 16:54:50 +08:00
更正 最长递增子序列 算法复杂度,添加O(nlogn)时间复杂度算法
This commit is contained in:
@ -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;}
|
||||||
|
|
||||||
|
//初始化list,len记录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)
|
||||||
|
Reference in New Issue
Block a user