LeetCode | 300. Longest Increasing Subsequence Dp

Given an unsorted array of integers, find the length of longestincreasing subsequence.

For example,
Given 
[10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is 
[2, 3, 7, 101],therefore the length is 4. Note that there may bemore than one LIS combination, it is only necessary for you to return thelength.

Your algorithm should run in O(n2) complexity.

Follow up: Could you improve it to O(n log n)time complexity?

Credits:
Special thanks to 
@pbrother foradding this problem and creating all test cases.

这一题是传统的求某个序列的最长递增子序列的题目,但是假如使用一般的DP去做的话时间或达到O(n ^ 2),题目中说了,要求使用O(nlog(n))的时间解决题目,因此我们需要考虑一种特殊的DP方法来解决题目

定义一个数组Dp,假设数组里面有Dp[1 ~ n]的值,给定的数组为nums,Dp[1 ~ n]对应于nums[1 ~ m]的范围,Dp[i]的具体含义为,在nums[1~m]的范围取递增子序列,可以找到的所有长度为i的子序列中末尾元素最小的伪Dp[i],

因此可以很容易的知道,len(Dp)表示nums[1 ~m]范围的最长子序列的长度,Dp[n]表示所有最长子序列中末尾的最小元素

知道了这一点之后我们可以遍历nums,动态的改变Dp,假设这个时候Dp里面有n个元素,下面分三种情况考虑

1)       当nums[i] > Dp[n]的时候,最长子序列的长度加一,最长子序列长为n +1,且长度为n + 1的子序列中末尾元素最小的是nums[i],因此Dp[n + 1] = nums[i]

2)       当nums[i] < Dp[0]的时候,nums[i]比Dp里面的所有元素都要小,因此更新Dp里面长度为1的最长子序列中的末尾最小的元素,也就是Dp[0] = nums[i]

3)       当Dp[0] < nums[i] < Dp[n]的时候,使用二分搜索在Dp[1 ~ n]的范围中找到一个j,使得theDp[j] >nums[i],theDp[j – 1] <nums[i],这个时候可以证明,nums[i]的值为长度为j的子序列中新的最小的末尾元素,也可以证明,nums[i]不会改变theDp[1 ~ j-1]和theDp[j+1 ~ n]的元素,只需要改变theDp[j]的元素

另外特别需要注意一点就是,当nums[i]和theDp[]中任何一个元素相等的时候,直接跳过这个数字,因为nums[i]不可能改变当前范围任何长度的递增子序列的最小的末尾值

该算法在最坏情况下时间复杂度为O(nlongn)

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        theL = len(nums)

        if theL <= 0: return 0

        theDp = [0 for i in range(theL + 1)]
        index = 0
        theDp[0] = nums[0]
        for i in range(theL):
            if nums[i] > theDp[index]:
                index += 1
                theDp[index] = nums[i]
            elif nums[i] < theDp[0]:
                theDp[0] = nums[i]
            else:
                if nums[i] == theDp[0] or nums[i] == theDp[index]: continue
                left = 0
                right = index

                while True:
                    mid = (right + left) // 2
                    if theDp[mid] == nums[i]: break
                    elif theDp[mid] < nums[i]:
                        if nums[i] < theDp[mid + 1]:
                            theDp[mid + 1] = nums[i]
                            break
                        left = mid + 1
                    else:
                        if nums[i] > theDp[mid - 1]:
                            theDp[mid] = nums[i]
                            break
                        right = mid - 1
        return index + 1

猜你喜欢

转载自blog.csdn.net/u012737193/article/details/80100770