题目链接
https://leetcode.com/problems/longest-increasing-subsequence/
题目描述
给定整数数组nums,返回最长严格递增子序列的长度。子序列是由数组派生得到的,子序列中的元素相对顺序不会改变。如[3,6,2,7]是数组[0,3,1,6,2,2,7]的子序列。
示例
Input:nums = [10,9,2,5,3,7,101,18]
Output: 4
最长严格递增子序列为[2,5,7,101],长度为4
输入:nums = [0,1,0,3,2,3]
输出:4
最长严格递增子序列为[0,1,2,3],长度为4
解题思路一
题目可用动态规划的思想来解决。定义d[i]为以nums[i]为末尾(必须包括)的最长递增子序列的长度。那么在已知d[0],d[1],...d[i-1]的情况下,能够推算出状态转移方程:d[i] = max(d[j])+1, 其中0<=j<i,且nums[j]<nums[i]。
状态d[i]将从d[j]转移过来,因此需要保证nums[j]<nums[i],这样nums[i]加入到nums[j]后才能构成严格递增子序列。最后返回d数组的元素最大值。
在代码实现过程中,需要双层for循环。第一层for循环更新d[i],第二层for循环找到符合条件的d[j]。
解决思路一Python实现
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
d = []
result = 1
for i in range(len(nums)):
d.append(1)
for j in range(i):
if nums[j] < nums[i]:
d[i] = max(d[i],d[j]+1) #如果条件不被触发则d[i]等于1,如果条件被触发,d[i]等于d[j]+1的最大值
result = max(result,d[i])
return result
时间复杂度与空间复杂度
由于用到了双重循环,时间复杂度为O(n^2);
由于算法在执行过程中会开辟最长长度为n的数组d,因此空间复杂度为O(n)。
解题思路二
此题可借助Patience sorting来解决。耐心排序算法起源于一个纸牌游戏,在游戏中纸牌将按照一定的规则逐个被分到对应的pile里。排序的关键在于建桶和入桶规则:
建桶规则:如果当前没有桶或者不符合入桶规则,则新建一个桶。
入桶规则:只要比桶里最上边的数字小即可入桶,如果有多个桶可入,只放入最左侧的桶中。
在算法中,贪心策略体现在“每个数字放在符合入桶规则的最左侧的桶中”,在算法执行的任意步骤中,桶顶上的元素值都是从左到右递增的。
对于最长递增子序列问题,桶的个数就是最长递增子序列的长度。关于纸牌的例子详见Princeton lecture。
下面用示例中的数组nums = [10,9,2,5,3,7,101,18]来举例讲解算法步骤:
解题思路二Python实现
在代码实现过程中,用一个数组result存储所有的桶顶元素,在遍历给定的nums过程中更新result数组。具体算法流程如下:
(1)如果nums[i]> result[-1],说明需要新建一个桶,直接将nums[i]追加到result数组中。
(2)如果nums[i]<=result[-1],则在result数组中找到第一个不小于nums[i]的元素(由于result数组中元素为递增且为寻找下界,因此这里可以用二分搜索的第二个模板,参见【Leetcode-Python】-二分搜索模板汇总与相关题目),并用nums[i]替换掉该元素。
class Solution:
def binarySearch(self,result,target): #返回result中第一个不小于target的元素 即是求x>= target的下界
left,right = 0,len(result)
while(left < right):
mid = (left + right) // 2
if (result[mid] == target):
return mid
elif(result[mid] < target):
left = mid + 1
else:
right = mid
return left #这里不用对nums[left]做判定, 因为一定是符合要求的。
def lengthOfLIS(self, nums: List[int]) -> int:
result = []
for i in range(len(nums)):
if len(result) == 0 or nums[i]>result[-1]:
result.append(nums[i])
else:
target_index = self.binarySearch(result,nums[i])
result[target_index] = nums[i]
return len(result)
时间复杂度与空间复杂度
遍历数组时间复杂度为O(n),执行二分搜索的时间复杂度为O(logn),因此整体算法的时间复杂度为O(nlogn)
由于算法在执行过程中会开辟最长长度为n的数组result,因此空间复杂度为O(n)。
拓展
如果需要进一步给出最长严格递增子序列的内容,解题思路二中得到的result数组即为结果。