最长递增子序列——附手算过程

题目来源

牛客题目:NC91 最长递增子序列
LeetCode上类似题目:第300题
但LeetCode上的题目只要求返回长度,牛客上却要求返回这个数组

解法思路

1. 比较好理解的O(n^2)复杂度解法

遍历数组,为数组中每一个数x,计算出:以x为结尾的最长递增子序列的长度m。
计算方法,对于x之前所有小于x的数,它们的m已经计算好了,找出其中最大值再+1即可。
这种算法比较容易想到,但时间复杂度还不够快,而且如果要返回这个子序列则比较麻烦。

2. 动态规划+二叉查找 O(nlogn)

设计一个数组list,其中的第 i 位代表:在原数组的长度为 i 的所有递增子序列中,最后一位最小是多少。
遍历原数组,依次填充list。
填充方法:对于原数组中的每个数字x,找到list中大于x的最小值,并替换它。如果没有,则将x加在list末尾。由于x是有序的,所以这里的查找可以采用二分查找。
最后得到的list的长度即为 最长递增子序列的长度。

如何找到最长递增子序列的内容?(用res数组表示)
在填充list的过程中,记录原数组中的每一位填充到了list中的哪一位。然后从右向左遍历原数组,找到第一个填充到list的最后一位的那个数,即为最长递增子序列的最后一位res[len(list)-1]。继续向左,找到第一个填充到 len(list)-2 的数,即为res[len(list)-2]。一直到0。

手写过程

手写过程可以看我传到b站的视频

代码

func LIS( arr []int ) []int {
    list := []int{arr[0]}
    link := make([]int, len(arr))
    link[0] = 0
    for i, n := range arr {
        if i == 0 {
            continue
        }
        if list[len(list)-1] < n {
            list = append(list, n)
        }
        l, r := 0, len(list)-1
        for l < r {
            m := l + (r-l)>>1
            if list[m] < n {
                l = m+1
            } else {
                r = m
            }
        }
        list[l] = n
        link[i] = l
    }
    res := make([]int, len(list))
    for i, j:=len(arr)-1, len(list)-1; i>=0; i-- {
        if link[i] == j {
            res[j] = arr[i]
            j--
        }
    }
    return res
}

你可能感兴趣的:(最长递增子序列——附手算过程)