Given an unsorted array of integers, find the length of longest increasing subsequence.
Example:
Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
Note:
- There may be more than one LIS combination, it is only necessary for you to return the length.
- Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?
LeetCode:链接
第一种方法:动态规划(Dynamic Programming),暴力枚举,时间复杂度O(n^2)
-
此时dp得到的是最长上升子序列,dp中的数字是以第 i个数字结尾的最长上升子序列 。
状态转移方程:dp[x] = max(dp[x], dp[y] + 1) 其中 y < x 并且 nums[x] > nums[y]。
首先遍历找到dp数组中的最大值maxlen以及下标index,其中maxlen就是最长递增子序列的长度,arr[index]就是最长递增子序列的最后一个数字,然后从index向前遍历数组arr,找到比arr[index]小的数arr[j]并且dp[j] + 1 = dp[index],这个值就是子序列的倒数第二个数,依次向前遍历即可得到最长递增子序列。
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
length = len(nums)
dp = [1] * length
for i in range(length):
for j in range(i):
# 如果nums[i] > nums[j] 才可以有上升子序列
if nums[i] > nums[j]:
# 更新的时候要和当前存储的子序列值比较 选择最大的
dp[i] = max(dp[i], dp[j]+1)
# 不一定最后的就是最大的
# 返回长度
return max(dp) if dp else 0
# 如果生成子序列 返回dp
# return dp
# 生成一个上升子序列
def generate(self, nums):
dp = self.lengthOfLIS(nums)
maxlen = dp[0]
index = 0
# 记录最长的子序列长度和对应的索引值
for i in range(1, len(dp)):
if dp[i] > maxlen:
maxlen = dp[i]
index = i
# 生成最长子序列长度的数组
result = [1 for i in range(maxlen)]
result[maxlen-1] = nums[index]
maxlen -= 1
# 从后往前遍历赋值
for i in range(index, -1, -1):
# 不仅要小于nums[index]同时还要保证dp的关系
if nums[i] < nums[index] and dp[i] + 1 == dp[index]:
result[maxlen-1] = nums[i]
maxlen -= 1
index = i
return result
# 生成全部的上升子序列
def generate1(self, nums):
dp = self.lengthOfLIS(nums)
# 得到最长的子序列长度
maxlen = max(dp)
all_index = []
# 找出所有最长子序列的索引值
for i in range(len(dp)):
if dp[i] == maxlen:
all_index.append(i)
result = []
# 从后往前遍历赋值
for i in all_index:
length = maxlen
# 生成最长子序列长度的数组
res = [1 for i in range(length)]
index = i
res[length-1] = nums[index]
length -= 1
# 从后往前遍历赋值
for j in range(index, -1, -1):
# 不仅要小于nums[index]同时还要保证dp的关系
if nums[j] < nums[index] and dp[j] + 1 == dp[index]:
res[length-1] = nums[j]
length -= 1
index = j
result.append(res)
return result
a = Solution()
print(a.generate1([10,9,2,5,3,7,101,18]))
第二种方法:动态规划 + 二分法。时间复杂度O(n * log n)
-
此时dp得到的不是最长上升子序列,dp中的数字存储的是对应长度LIS的最小末尾。我们需要记录的是子序列的最小末元素,有了这个末尾,我们就可以一个一个地插入数据。
显然,dp[i]的值越小,对于添加值更有利。因此,dp数组的元素个数即是LIS的答案。我们先建立一个数组dp,把首元素放进去,然后比较之后的元素。
- 如果遍历到的新元素比dp数组中的首元素小的话,替换首元素为此新元素,
- 如果遍历到的新元素比dp数组中的末尾元素还大的话,将此新元素添加到dp数组末尾(注意不覆盖原末尾元素)。
- 如果遍历到的新元素比dp数组首元素大,比尾元素小时,通过二分查找的方法在一个从小到大的数组中找到第一个大于等于新元素的数,然后用新元素替换这个数。[1 2 3 6 8 7]对应的dp是[1 2 3 6 7];[1 2 3 6 8 6]对应的dp是[1 2 3 6 8]。
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
dp = [nums[0]]
for i in range(1, len(nums)):
if nums[i] < dp[0]:
dp[0] = nums[i]
elif nums[i] > dp[-1]:
dp.append(nums[i])
elif nums[i] > dp[0] and nums[i] < dp[-1]:
low = 0
high = len(dp) - 1
while low <= high:
mid = (low + high) // 2
if nums[i] > dp[mid]:
low = mid + 1
# 如果nums[i]和nums[mid]相等,就直接让high=mid-1,跳出循环
else:
high = mid - 1
dp[low] = nums[i]
return len(dp)