From abb9cfd5be04acf55f41f0438abe43b587354b3e Mon Sep 17 00:00:00 2001 From: liyao <-> Date: Sat, 14 Aug 2021 16:24:20 +0100 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=AD=A3=20=E6=9C=80=E9=95=BF?= =?UTF-8?q?=E9=80=92=E5=A2=9E=E5=AD=90=E5=BA=8F=E5=88=97=20=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E5=A4=8D=E6=9D=82=E5=BA=A6=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?O(nlogn)=E6=97=B6=E9=97=B4=E5=A4=8D=E6=9D=82=E5=BA=A6=E7=AE=97?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0300.最长上升子序列.md | 124 ++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 57edd13e..1161e158 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -33,7 +33,7 @@ * 1 <= nums.length <= 2500 * -10^4 <= nums[i] <= 104 - +## 方法一 动态规划 ## 思路 最长上升子序列是动规的经典题目,这里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(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=list[j],那么我们可以在list[j]对应的子序列中删除最后j-i个元素得到长度与list[i]相同的子序列,其最右端的值maxlist[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<=jnums[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 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(Listlist, 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) 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]