1、题目描述
https://leetcode-cn.com/problems/longest-increasing-subsequence/
给定一个无序的整数数组,找到其中最长上升子序列的长度。
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
相关题:673. 最长递增子序列的个数(有几组最长递增子序列)
2、代码详解(拓:打印出这个LIS)
法一:DP,O(N^2),掌握
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums: return 0
dp = [1] * len(nums)
for i in range(len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
nums = [10, 9, 2, 5, 3, 7, 101, 18]
s = Solution()
print(s.lengthOfLIS(nums))
打印出这个序列
- 开一个pi决策数组,记录从哪一个状态转移过来的(记录上一个状态的下标)
- 记录结束标志pEnd
- 从结束标志开始,反向找前一个状态
class Solution(object):
def lengthOfLIS(self, nums):
if not nums: return 0
n = len(nums)
# dp[i] 表示以nums[i]为结尾的最长上升子序列的长度
dp = [1] * n
pi = [-1] * n # 决策数组,记录前一次选的哪一个
pEnd = 0 # 结束标志
res = 0
for i in range(n):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
if dp[j] + 1 == dp[i]: # 产生更新
pi[i] = j # 决策数组记录, 从j转移过来
res = max(res, dp[i]) # res记录最大值
if dp[i] == res: # 如果是当前最大,记录一下结束标志pEnd
pEnd = i
seq = [-1] * res # 打印序列数组
for i in range(res-1, -1, -1): # 反向找前一个状态
seq[i] = nums[pEnd]
pEnd = pi[pEnd] # 更新为前一个
print(pi)
print(seq)
return res
nums = [10, 9, 2, 5, 3, 7, 101, 18]
nums2 = [4, 2, 4, 5, 3, 7]
s = Solution()
print(s.lengthOfLIS(nums))
print(s.lengthOfLIS(nums2))
输出结果
[10, 9, 2, 5, 3, 7, 101, 18] # 原始
[-1, -1, -1, 2, 2, 4, 5, 5] # pi决策数组
[2, 3, 7, 18] # 所求LIS
4 # 长度
[4, 2, 4, 5, 3, 7] # 原始
[-1, -1, 1, 2, 1, 3] # pi决策数组
[2, 4, 5, 7] # LIS
4 # 长度
法二:二分查找,O(NlogN),了解
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
tails = [0] * len(nums)
size = 0
for x in nums:
i, j = 0, size
while i != j:
m = (i + j) / 2
if tails[m] < x:
i = m + 1
else:
j = m
tails[i] = x
size = max(i + 1, size)
return size
代码链接:
思路链接:纸牌游戏(耐心排序)