题目描述:给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。说明一下,最长上升子序列的定义:最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。
样例:
给出[5,4,1,2,3],这个LIS是[1,2,3],返回 3
给出[4,2,4,5,3,7],这个LIS是[4,4,5,7],返回 4
对一个无序的数列求取最长上升子序列需要用动态规划的方法解决,这个属于另一个专题的东西了,所以暂时先不讲。但是此处有一点非常特殊,就是我们现在所面对的这道题目,它并不要求解除最长上升子序列的具体内容,而只需要我们求出长度即可。
因此,我们可以建立这样一个栈,栈在初始构建时,只要新元素大于栈顶元素,则被加入栈。例如数组[1, 5, 8, 3, 6, 7 ],我们依次扫描数组的元素:
1. 初始化空栈stack
2. 扫描到1,stack.append(1)
3. 依次扫描,直到8,此时stack = [1, 5, 8],因为每个扫描到的新元素都是大于栈顶元素的
4. 扫描到3,发现其不大于栈顶元素5。
好了,我们把思路先暂停一下。现在建立好的这个栈,实际上保存了从数组第一个元素开始的最长上升子序列,但是这不是我们要求的。那么接下来怎么办呢?我们用小于栈顶的新元素替换栈中大于该新元素的最小的元素,在这个例子中,用3替换5。这样一来,实际上,栈所保存的依旧是一个上升子序列。而且具备以下两个特征:
(1) stack是单调递增的
(2) stack中元素的个数没变(因为只是在替换)
然后,按照这个逻辑我们接着处理数组[1, 5, 8, 3, 6, 7 ]:
5. 用3替换5,stack = [1, 3, 8]
6. 6 < 8,用6替换8,stack = [1, 3, 6]
7. 7 > 6,stack.append(7),stack = [1, 3, 6, 7]
7. 返回stack的长度即可
实际上,碰到比栈顶元素小的元素,就替换的目的在于存储了一种在之后出现最长上升子序列的“可能性”,而这种“可能性”只有在真正超出当前栈的时候才会被实现,否则,在子序列长度上就一直保持当前的不变。
按照这个思路写出代码:其中查找大于当前元素的最小元素,用二分法实现。
class Solution:
"""
@param nums: The integer array
@return: The length of LIS (longest increasing subsequence)
"""
def longestIncreasingSubsequence(self, nums):
if len(nums) == 0:
return 0
stack = [nums[0]]
for i in nums[1:]:
size = len(stack)
if i >= stack[size - 1]:
stack.append(i)
else:
index = self.binarysearchlarger(stack, i)
stack[index] = i
return len(stack)
def binarysearchlarger(self, stack, target):
left, right = 0, len(stack) - 1
while left <= right:
mid = (left + right) // 2
if stack[mid] > target:
right = mid - 1
else:
left = mid + 1
return left
# write your code here
此算法时间复杂度为O(nlog(n))