【题目】
当我们给定一个简单序列[1、5、2、4、3],现在要求输出该序列中,最大子序列的长度,子序列要求从小到大,元素下标可以跳跃,例如子序列[1、2、3]。
【实现方式(暴力实现)】
1、先定义一个函数L(nums,i),
(1)传入参数为全部子序列和某一个元素的下标,
(2)作用是找到以该元素为首元素,最长子序列的长度,并返回这个长度
2、再定义一个函数length_of_LIS(nums),
(1)传入参数为全部序列,
(2)作用是循环调用L(nums,i)函数,依次将每个元素的下标传入,得到L(nums,i)函数的返回值,再取返回值的最大值,作为最大子序列的长度,
3、在if __name__=="__main__":中调用length_of_LIS(nums)函数,传入被查找序列,得到其最大子序列的长度,
【实现代码(暴力实现)】
"""
最长子序列
"""
def L(nums, i):
"""
传入参数为全部序列和某一个元素的下标,
该函数的作用是,找到以该下标元素为首元素的最长子序列
it will return the max number from your list
:return:Null
"""
if i == len(nums) - 1:
return 1
# 初始化子系列的长度为1
max_len = 1
for j in range(i + 1, len(nums)):
if nums[i] < nums[j]:
max_len = max(max_len, L(nums, j) + 1)
return max_len
def length_of_LIS(nums):
"""
传入参数为全部序列,和每一个元素的下标
调用L(nums,i)函数,得到以(每个元素为首元素的)最长子序列的长度,取其最大值,返回
:param nums:
:return:
"""
return max(L(nums, i) for i in range(len(nums)))
if __name__ == '__main__':
nums = [1, 5, 2, 4, 3]
print(length_of_LIS(nums))
【时间time】
可以看到,暴力算法的时间大概在0.66秒左右,
【实现方式(动态规划)】
1、初始化一个字典things,
(1)key:存储下标
(2)value:存储以key为下标的元素作为首元素,所能组成的最长子序列的长度
2、在调用L(nums,i)时,先查找things字典的key是否有i,如果有,查看以该元素为首元素,所形成的最长子序列的长度,并将其返回,这样会节省很大运行时间。
【实现代码(dp算法)】
"""
最长子序列
"""
import time
# 定义things字典,key存储序列的元素,value存储该key元素,所能组成的最长的子序列的长度
things = {}
def L(nums, i):
"""
传入参数为全部序列和某一个元素的下标,
该函数的作用是,找到以该下标元素为首元素的最长子序列
it will return the max number from your list
:return:Null
"""
# 查询下标为i的元素,以其为首元素所组成的最长子序列的长度
if i in things:
return things[i]
if i == len(nums) - 1:
return 1
# 初始化子系列的长度为1
max_len = 1
for j in range(i + 1, len(nums)):
if nums[i] < nums[j]:
max_len = max(max_len, L(nums, j) + 1)
return max_len
def length_of_LIS(nums):
"""
传入参数为全部序列,和每一个元素的下标
调用L(nums,i)函数,得到以(每个元素为首元素的)最长子序列的长度,取其最大值,返回
:param nums:
:return:
"""
return max(L(nums, i) for i in range(len(nums)))
if __name__ == '__main__':
start_time = time.time()
nums = [1, 5, 2, 4, 3]
print(length_of_LIS(nums))
end_time = time.time()
print(f"运行时间为{end_time - start_time}")
【时间time】
在使用了dp算法之后,时间大致在0.35秒左右