首页 > 试题广场 >

最长上升子序列(三)

[编程题]最长上升子序列(三)
  • 热度指数:75124 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定数组 arr ,设长度为 n ,输出 arr 的最长上升子序列。(如果有多个答案,请输出其中 按数值(注:区别于按单个字符的ASCII码值)进行比较的 字典序最小的那个)

数据范围:
要求:空间复杂度 ,时间复杂度
示例1

输入

[2,1,5,3,6,4,8,9,7]

输出

[1,3,4,8,9]
示例2

输入

[1,2,8,6,4]

输出

[1,2,4]

说明

其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个 按数值进行比较的字典序 最小,故答案为(1,2,4)         

备注:

1 直接用动态规划,O(n*n)超时了。
    vector<int> LIS(vector<int>& arr) {
        vector<int> dp(arr.size(), 1);
        vector<int> pp(arr.size(), 0);
        int maxs = 0, maxi = 0;
        for(int i = 1; i < arr.size(); i++) {
            int lmax = 1;
            pp[i] = i; // to self
            for(int k = i-1; k >= 0; k--) {
                if(arr[i] > arr[k] && (lmax < dp[k]+1 || 
                                      (lmax == dp[k]+1 && arr[k] < arr[pp[i]])) ) {
                    lmax = dp[k]+1;
                    pp[i] = k;
                } else if(k < lmax) {
                    break;
                }
            }
            dp[i] = lmax;
            if(maxs < lmax || (maxs == lmax && arr[i] < arr[maxi]) ) {
                maxs = lmax;
                maxi = i;
            }
        }
        vector<int> vres;
        for(int k = maxi; true; k = pp[k]) {
            vres.insert(vres.begin(), arr[k]);
            if(k == pp[k]) break;
        }
        return vres;
    }
2 动态规划+二分
上面的动态规划算法需要向前遍历找到前一个k,导致算法复杂度为O(n*n),一个牛逼的想法是使用二分查找优化,使复杂度降低为O(n*logn)。
基本点在于维护一个数组maxEnd,其元素maxEnd[k]表示长度为k+1的递增长子序列的最后一个元素,并且是字典序最小的那个。显然maxEnd是一个递增数组。
1 如果arr[i] > maxEnd.back()时,显然直接..., maxEnd.back(), arr[i]依然是一个递增子序列。
2 问题是arr[i] <= maxEnd.back()时怎么处理?
我们找到maxEnd中大于等于arr[i]的元素位置k,将maxEnd[k]替换为arr[i],这表示将长度为k+1的子序列的最后一个元素更新为更小的值。显然更合理。然后再同步更新dp[k]值的为i。
vector<int> LIS(vector<int>& arr) {
        if(arr.size() < 2) return arr;
        vector<int> dp(arr.size(), 1);
        vector<int> maxEnd(1, arr[0]);
        for(int i = 1; i < arr.size(); i++) {
            if(arr[i] > maxEnd.back()) { // in inc seq
                dp[i] = maxEnd.size()+1;
                maxEnd.push_back(arr[i]);
            } else { // ai < maxEnd
                auto pos = std::lower_bound(maxEnd.begin(), maxEnd.end(), arr[i]);
                int idx = pos - maxEnd.begin();
                maxEnd[idx] = arr[i];
                dp[i] = idx + 1;
            }
        }
        int len = maxEnd.size();
        vector<int> vres(len);
        for(int i = dp.size()-1; i >= 0; --i) {
            if(dp[i] == len) {
                vres[len-1] = arr[i];
                --len;
            }
        }
        return vres;
    }


发表于 2020-10-25 11:23:19 回复(2)
首先感谢 华科不平凡 提供的思路。这个Java的实现完全按照该思路实现,只是具体细节略有不同。
至于整个思路的原理,不是很好说清楚,能记住关键点的操作就行了😅
public class Solution {
    public int[] LIS (int[] arr) {
        int n = arr.length;
        if (n == 0)    //特判空情况,方便后面处理
            return new int[0];
        
        int[] ls = new int[n];    //注意不能用list,会超时。所以直接开一个大数组,动态扩张
        int[] dp = new int[n];
        
        int l = 1;    //ls递增序列的实际长度
        ls[0] = arr[0];
        dp[0] = 1;
        
        for (int i = 1; i < n; i++) {
            if (ls[l - 1] < arr[i]) {
                ls[l++] = arr[i];    //直接加入序列
                dp[i] = l;    //dp[i]对应的序列是整个ls
            }
            else {
                int j = 0, k = l - 1;
                int ind = 0;
                //找ls中第一个 >= arr[i]的(必定存在,因为保证ls的最后一个 >= arr[i])
                while (j <= k) {
                    int m = j + (k - j) / 2;
                    if (ls[m] >= arr[i]) {
                        ind = m;
                        k = m - 1;
                    }
                    else {
                        j = m + 1;
                    }
                }
                ls[ind] = arr[i];    //找到位置后替换掉,注意是替换不是插入
                //ls序列的总长不变,但是为了复用原序列一些 < arr[i]的结果,选择二分把arr[i]替换到合适的位置
                //所以dp[i]对应的序列其实是ls的[0, ind]部分(要保证序列的最后是arr[i]),即长度为ind + 1
                dp[i] = ind + 1;
            }
        }
        
        //这里其实可以复用ls来填充的,但是用ans是为了说明求最后的子序列和ls无关
        //ls只是为了上面求dp的复杂度从O(n ^ 2)降低为O(n * logn)
        //这里的求法是倒着遍历,找到满足长度的dp就记录,然后更新。(即同样值的dp,选尽量靠右边的)
        int[] ans = new int[l];
        for (int i = n - 1, j = l; i >= 0; i--) {
            if (dp[i] == j) {
                ans[--j]  = arr[i];
            }
        }
        
        return ans;
    }
}


发表于 2021-03-20 00:13:04 回复(3)
思路跟大家一样,直接用treeset
    public int[] LIS (int[] temp) {
        int N = temp.length;
        int[] dp = new int[N];
        Arrays.fill(dp, 1);
        TreeSet<Integer> set = new TreeSet<>();
        set.add(temp[0]);
        for(int i = 1; i < dp.length; i++) {
            if(temp[i] > set.last()) {
                set.add(temp[i]);
                dp[i] = set.size();
            } else {
                int first = set.ceiling(temp[i]);
                set.remove(first);
                set.add(temp[i]);
                dp[i] = set.headSet(temp[i]).size() + 1;
            }
        }
        int[] res = new int[set.size()];
        for(int i = temp.length - 1, j = set.size(); i >= 0; i--) {
            if(dp[i] == j) {
                res[--j] = temp[i];
            }
        }
        return res;
    }


发表于 2021-07-16 10:01:54 回复(1)
python3有bug,直接返回数组都会显示超时。。。
class Solution:
    def LIS(self, arr):
        # write code here
        return arr

发表于 2020-12-04 12:26:15 回复(9)
一开始的思路是将数组排序,然后和原来的数组比较,寻找最长公共子序列,使用动态规划,但是这样会超出内存限制,后来看完题解换了一种方法过了。
import java.util.*;
public class Solution {
    public int[] LIS (int[] arr) {
        if(arr == null || arr.length <= 0){
            return null;
        }

        int len = arr.length;
        int[] count = new int[len];             // 存长度
        int[] end = new int[len];               // 存最长递增子序列

        //init
        int index = 0;                          // end 数组下标
        end[index] = arr[0];
        count[0] = 1;

        for(int i = 0; i < len; i++){
            if(end[index] < arr[i]){
                end[++index] = arr[i];
                count[i] = index;
            }
            else{
                int left = 0, right = index;
                while(left <= right){
                    int mid = (left + right) >> 1;
                    if(end[mid] >= arr[i]){
                        right = mid - 1;
                    }
                    else{
                        left = mid + 1;
                    }
                }
                end[left] = arr[i];
                count[i] = left;
            }
        }

        //因为返回的数组要求是字典序,所以从后向前遍历
        int[] res = new int[index + 1];
        for(int i = len - 1; i >= 0; i--){
            if(count[i] == index){
                res[index--] = arr[i];
            }
        }
        return res;
    }
}



import java.util.*;

/**
 * 问题可以转化为求该序列和已排好序的序列的最长公共子序列
 * 用动态规划轻松搞定
 * 注意字典序,需要反向比较
 * 需要设置标志位,以便后面打印输出
 */

public class Solution {
    int[] res;
    int index = 0;

    public int[] LIS (int[] arr) {
        if(arr == null || arr.length <= 0){
            return res;
        }
        reverse(arr);
        int[] nums = arr.clone();
        Arrays.sort(nums);
        reverse(nums);

        int n = arr.length;
        int[][] flags = new int[n + 1][n + 1];//用于打印输出

        int len = LCS(nums,arr, flags, n);

        res = new int[len];
        helper(nums, flags, res, n, n);

        reverse(res);
        return res;
    }

    //找到最大长度,并标记(方便存储打印)
    public int LCS(int[] nums1, int[] nums2, int[][] flags, int n){
        int[][] dp = new int[n + 1][n + 1];//记录最大长度
        for(int[] a : dp){
            Arrays.fill(a, 0);
        }
        int max = Integer.MIN_VALUE;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if(nums1[i - 1] == nums2[j - 1]){
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    flags[i][j] = 1;                    //相等设置为 1
                }
                else if(dp[i - 1][j] > dp[i][j - 1]){
                    dp[i][j] = dp[i - 1][j];               //选大的
                    flags[i][j] = 2;
                }
                else{
                    dp[i][j] = dp[i][j - 1];
                    flags[i][j] = 3;
                }
                max = Math.max(max, dp[i][j]);
            }
        }
        return max;
    }

    //存起来
    public void helper(int[] nums,int[][] flags, int[] res,  int i, int j){
        if(i == 0 || j == 0){
            return;
        }
        if(flags[i][j] == 1){
            helper(nums, flags, res, i - 1, j - 1);
            res[index++] = nums[i - 1];
//            System.out.print(nums[i - 1] + " ");
        }
        else if(flags[i][j] == 2){
            helper(nums, flags, res, i - 1, j);
        }
        else{
            helper(nums, flags, res, i, j - 1);
        }
    }

    public void reverse(int[] nums){
        int left = 0;
        int right = nums.length - 1;
        while(left < right){
            swap(nums, left, right);
            left ++;
            right--;
        }
    }

    public void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}


编辑于 2020-09-06 10:33:15 回复(2)
这题。。。。我面试美团以及小米算法岗都考到了。。。 先从求最长子序列长度的题目过渡到这题 
发表于 2021-03-25 17:53:32 回复(4)
python解法(二分+贪心)
牛客的python有bug怎么都会超时,这里已通过本地IDE
二分+贪心的思想可以见力扣官解,这里与力扣不一样的地方在于需要输出字典序的结果
怎么输出呢?
1、找到列表l中最大值第一次出现的索引(这里记为max_index)
2、从max_index往前遍历l,每当元素第一次变小,就记录其索引tmp,注意再继续遍历时,要找的是比l[tmp]小的元素第一次出现时的索引
3、以上操作是从后往前的,因此最后要reverse一下,就是最终结果
为什么上述输出就是最终结果?见@华科不平凡 对他所讲的第二步的解释
# l[i]记录以A[i]结束的最长递增子序列的长度
# res为最长递增子序列,当存在两个或以上的序列时,返回字典序最小的那个
import bisect
class Solution:
    def LIS(self , A ):
        # write code here
        d = []
        c = 0
        l = []
        for a in A:
            i = bisect.bisect_left(d, a)  # 二分
            if i < len(d):
                l.append(l[A.index(d[i])])
                d[i] = a
            elif not d or d[-1] < a:
                d.append(a)
                c += 1
                l.append(c)
        print(l)
        print(d)  # 此时最长递增子序列的长度已求出,即为len(d),但没求出最长递增子序列本身的样子
        res = [A[l.index(max(l))]]
        tmp = max(l) - 1
        for i in range(l.index(max(l)) - 1, -1, -1):
            if tmp == 1 and l[i] == tmp:
                res.append(A[i])
                break
            if l[i] == tmp:
                res.append(A[i])
                tmp -= 1
        res.reverse()
        return res

编辑于 2021-03-22 16:56:00 回复(0)
import java.util.*;

//动态规划+二分查找
public class Solution {
    
    public int[] LIS(int[] arr) {
        // write code here
        List<Integer> cur = new ArrayList<>();
        cur.add(arr[0]);
        int[]dp = new int[arr.length];
        dp[0]=1;
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > cur.get(cur.size() - 1)) {
                cur.add(arr[i]);
                dp[i] = cur.size();
            } else {
                dp[i] = put(cur, arr[i])+1;
            }
        }
        int[] ans = new int[cur.size()];
        int j = cur.size(); 

        //最小的那组就得这么找
        for(int i=dp.length-1;i>=0;i--){
            if(dp[i]==j){
                ans[--j] = arr[i];
            }
        }

        return ans;
    }

    public int put(List<Integer> cur, int i) {
        int l = 0, r = cur.size()-1, m = 0;
        while (l <= r) {
            m = l + (r - l) / 2;
            if (cur.get(m) < i) {
                l = m + 1;
            } else {
                r = m - 1;
            }
        }
        cur.set(l, i);
        return l;
    }
}

发表于 2021-10-12 23:31:22 回复(0)
import java.util.*;


public class Solution {
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型一维数组 the array
     * @return int整型一维数组
     */
        public int[] LIS (int[] temp) {
        int N = temp.length;
        int[] dp = new int[N];
        Arrays.fill(dp, 1);
        TreeSet<Integer> set = new TreeSet<>();
        set.add(temp[0]);
        for(int i = 1; i < dp.length; i++) {
            if(temp[i] > set.last()) {
                set.add(temp[i]);
                dp[i] = set.size();
            } else {
                int first = set.ceiling(temp[i]);
                set.remove(first);
                set.add(temp[i]);
                dp[i] = set.headSet(temp[i]).size() + 1;
            }
        }
        int[] res = new int[set.size()];
        for(int i = temp.length - 1, j = set.size(); i >= 0; i--) {
            if(dp[i] == j) {
                res[--j] = temp[i];
            }
        }
        return res;
    }
}
发表于 2021-08-22 21:07:51 回复(1)
class Solution {
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    vector<int> LIS(vector<int>& arr) {
        // write code here
        //贪心+二分查找 O(nlogn)
        //维护一个数组vec,里面存放的是递增子序列
        //maxLen数组里存放的是以arr[i]为结尾的递增子序列的最大长度
        //核心思想:保证vec[]中的元素是符合条件中的最小的那一个。
        
        //第一步:利用贪心+二分求最长递增子序列长度,vec里存放的元素不一定是我们最终想要的结果,
        //但是vec里最终的数组大小就是所求的最长递增子序列的长度
        vector<int>vec;
        vector<int>maxLen;
        if(arr.size()<1) return vec;
        vec.emplace_back(arr[0]);
        maxLen.emplace_back(1);
        for(int i=1;i<arr.size();i++){
            if(arr[i]>vec.back()){
                vec.emplace_back(arr[i]);
                maxLen.emplace_back(vec.size());
            }else{
                 // lower_bound(begin, end, val)包含在<algorithm>中
                // 它的作用是返回有序数组begin..end中第一个大于等于val的元素的迭代器
                int pos=lower_bound(vec.begin(), vec.end(), arr[i])-vec.begin();
                vec[pos]=arr[i];
                maxLen.emplace_back(pos+1);
            }
        }
        //第二步:填充最长递增子序列 case:输入[2,4,6,1,5]
        int i=arr.size()-1,j=vec.size();
        for(;j>0;--i){
            if(maxLen[i]==j){
                vec[--j]=arr[i];
            }
        }
        return vec;
    }
};

发表于 2021-08-10 11:20:18 回复(0)
import java.util.*;


public class Solution {
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型一维数组 the array
     * @return int整型一维数组
     */
    public int[] LIS (int[] arr) {
        if(arr==null||arr.length<=1){
            return arr;
        }
        int len = arr.length;
        int[] dp = new int[len];
        int[] tails = new int[len];
        dp[0]=1;
        tails[0]=arr[0];
        int size = 0;
        for(int i=1;i<len;++i){
            if(arr[i]>tails[size]){
                ++size;
                tails[size]=arr[i];
                dp[i]=size+1;
            }else{
                int j = binarySearch(tails,0,size,arr[i]);
                tails[j]=arr[i];
                dp[i]=j+1;
            }
        }
        
        int[] res = new int[size+1];
        for(int i = len-1,j=size;j>=0;--i){
            if(dp[i]==j+1){
                res[j--]=arr[i];
            }
        }
        return res;
    }
    
    private int binarySearch(int[] arr,int start,int end,int val){
        while(start<end){
            int mid = start+(end-start)/2;
            if(arr[mid]<val){
                start=mid+1;
            }else{
                end=mid;
            }
        }
        return arr[start]>=val?start:start+1;
    }
}

发表于 2021-06-04 18:44:27 回复(0)
看数据范围 n <= 10^5,O(N^2) 必定超时,所以只能采用 dp + 二分的方式:
class Solution {
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    vector<int> LIS(vector<int>& arr) {
        int n = arr.size();
        vector<int> q(n + 1);
        vector<int> f(n + 1);
        int len = 0;
        q[0] = -2e9;
        for (int i = 0; i < n; ++i) {
            int l = 0, r = len;
            while (l < r) {
                int mid = l + r + 1 >> 1;
                if (q[mid] < arr[i]) l = mid;
                else r = mid - 1;
            }
            f[i] = r + 1;  // 记录以第 i 个元素结尾的最长上升子序列的长度
            len = max(len, r + 1);
            q[r + 1] = arr[i];
        }
        vector<int> res(len);
        for (int i = f.size() - 1; i >= 0; --i) {
            if (f[i] == len) {
                res[len - 1] = arr[i];
                --len;
            }
        }
        return res;
    }
};


发表于 2021-06-01 21:15:48 回复(0)
为什么我动态规划超时!
class Solution {
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    //[1,2,8,6,4]
    vector<int> LIS(vector<int>& arr) {
        // write code here
        //res[i]j记录数组中0-i的子串 的 最长公公子序列路径
        //初始化为自己本身的值,
        vector<vector<int> > res;
        for(int i=0;i<arr.size();i++){
            vector<int> tmp;
            tmp.push_back(arr[i]);
            res.push_back(tmp);
        }
        //dp[i]记录数组中o-i的最长上升子序列的长度
        vector<int> dp(arr.size(),1);
        for(int i=0;i<arr.size();i++){
            vector<int> tmp;
            for(int j=0;j<i;j++){
                if(arr[j] < arr[i]){
                    vector<int> tt = res[j];
                    tt.push_back(arr[i]);
                    res[i]= tt;
                    dp[i]=max(dp[i],dp[j] +1);
                }
            }
        }
        int max=0;
        int index=0;
        for(int i=0;i<dp.size();i++){
            if(dp[i] > max){
                index=i;
                max=dp[i];
            }
        }
        vector<vector<int> > m;
        for(int i=0;i<dp.size();i++){
            if(dp[i] == max){
                m.push_back(res[i]);
            }
        }
        sort(m.begin(), m.end());
        return m[0];
        
    }
};

发表于 2021-05-30 17:56:13 回复(0)
import java.util.*;


public class Solution {
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型一维数组 the array
     * @return int整型一维数组
     */
//     arr=[2,1,5,3,6,4,8,9,7]
//     dp =[1,3  ,4,  7  ,9]
//     ans=[1,13,134,1347,13489]
    
    public int[] LIS (int[] arr) {
        // write code here
        int n = arr.length;
        StringBuilder[] ans = new StringBuilder[100005];
        ans[0] = new StringBuilder("");
        int[] dp = new int[100005];//dp[i]维护的是长度为i的最后一个数字的最小值
        int size = 0;
        for(int i=0;i<n;i++){
            int l = 0,r = size;
            while(l<r){ //二分找在dp的(l,r)中比arr[i]小的最大的数的下标
                int mid = (l+r+1)/2;
                if(arr[i]>dp[mid]){
                    l = mid;
                }else{
                    r = mid-1;
                }
            }
            size = Math.max(size,r+1);
//             System.out.println("r:"+r);
            dp[r+1] = arr[i]; //更新dp的r+1的位置的值为arr[i]
            StringBuilder sb = new StringBuilder(ans[r]);
            ans[r+1] = sb.append(String.valueOf(arr[i])+" ");  //将答案更新为ans[r]+arr[i]
        }
//         System.out.println(ans[size].toString());
        String[] res = ans[size].toString().split(" ");//将答案切分转成int类型
        int[] resint = new int[res.length];
        for(int i=0;i<res.length;i++){
            resint[i] = Integer.parseInt(res[i]);
        }
        return resint;
    }
}

发表于 2021-03-13 16:19:12 回复(0)
python为啥一直超时,折腾了半天
我复制别人已通过的代码都超时。。😓
发表于 2021-03-06 23:25:01 回复(1)
dp[i] 定义为长度为i字典序最小的递增子序列
arr=【5,4,6,2,3,7】
dp[0]={INT_MIN}
dp[1]={5}
dp[1]={4}
dp[2]={4,6}
dp[1]={2}
dp[2]={2,3}
状态转移就是 用arr[i] 和dp[j].back() 也就是dp数组最大值比较
if(arr[i] >dp[j].back())  dp长度也就是递增子序列长度增加,新建 dp[j+1]=dp[j]+{arr[i]}
if(arr[i]<dp[j].back())  j--,向前遍历dp[j].back 找到arr[i] >dp[j].back()后,更新dp[j+1]=dp[j]+{arr[i]}
初始状态dp[0]={INT_MIN},因为dp[1]长度为1字典序最小的递增子序列可能需要更新
class Solution {
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    vector<int> LIS(vector<int>& arr) {
        vector<vector<int>> dp;
        dp.push_back({INT_MIN});//dp[0]={INT_MIN}
        //初始状态dp[0]={INT_MIN},因为dp[1]长度为1字典序最小的递增子序列可能需要更新
        if (arr.empty()) return dp[0];
        dp.push_back({ arr[0] });
        for (int i = 1; i<arr.size(); i++)
        {        
            for (int len = dp.size(); len >= 0; len--)
            {
                //遍历dp从dp[len-1]开始遍历
                int tmp = dp[len-1].back();
                if (arr[i]>tmp&&len == dp.size())
                {//dp长度也就是递增子序列长度增加,新建 dp[j+1]=dp[j]+{arr[i]}
                    vector<int> dp_i = dp.back();
                    dp_i.push_back(arr[i]);
                    dp.push_back(dp_i);
                    break;
                }
                else if (arr[i]>tmp&&len<dp.size())
                {//向前遍历dp[len-1].back 找到arr[i] >dp[len-1].back()后,更新dp[len]=dp[len-1]+{arr[i]}
                    if (len == 1) dp[len].pop_back();
                    else dp[len] = dp[len - 1];
                    dp[len].push_back(arr[i]);
                    break;
                }
                
            }
        }
        return dp.back();
    }
};


编辑于 2020-09-04 10:32:29 回复(2)
感谢华科不平凡提供的贪心算法+二分查找以及如何寻找靠前子序列的思路,特奉上python3代码方便大家
#
# retrun the longest increasing subsequence
# @param arr int整型一维数组 the array
# @return int整型一维数组
#
import bisect
class Solution:
    def LIS(self , arr ):
        # write code here
        def tool(target):
            left = 0
            right = len(data)-1
            while left<=right:
                mid = left+(right-left)//2
                if data[mid]==target:
                    return mid
                elif data[mid]>target:
                    right = mid-1
                else:
                    left = mid+1
            return left
        data = [arr[0]]
        n = len(arr)
        max_len =[1]
        for i in range(1,n):
            if data[-1]<arr[i]:
                data.append(arr[i])
                max_len.append(len(data))
            else:
                temp = tool(arr[i])
                data[temp] = arr[i]
                max_len.append(temp+1)
        maxs = len(data)
        for i in range(len(arr)-1,-1,-1):
            if maxs == max_len[i]:
                data[maxs-1] = arr[i]
                maxs-=1
        return data

发表于 2021-08-16 10:49:45 回复(0)
这道题明明是求最长子序列,不是求最长子序列的长度,有些同学没仔细读题!!
发表于 2021-05-24 09:25:39 回复(0)

超时版:

function LIS( arr ) {
    if (arr === null || arr.length === 0) return 0;
    const dp = new Array(arr.length).fill(1);
    let max = 1;
    for (let i = 1; i < arr.length; i++) {
        for (let j = 0; j < i; j++) {
            if (arr[i] > arr[j]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
                max = Math.max(max, dp[i]);
            }
        }
    }
    const result = new Array(max);
    for (let i = dp.length - 1; i >= 0; i--) {
        if (dp[i] === max) result[--max] = arr[i];
    }
    return result;
}

通过版:

function LIS( arr ) {
    const list = new Array(arr.length + 1);
    const maxLen = new Array(arr.length);
    let n = 1;
    list[1] = arr[0];
    maxLen[0] = 1;
    for (let i = 0; i < arr.length; i++) {
        if (list[n] < arr[i]) {
            list[++n] = arr[i];
            maxLen[i] = n;
        } else {
            const index = binarySearch(list, n, arr[i]);
            list[index] = arr[i];
            maxLen[i] = index;
        }
    }
    const result = new Array(n);
    for (let i = maxLen.length - 1; i >= 0; i--) {
        if (maxLen[i] === n) result[--n] = arr[i];
    }
    return result;
}
function binarySearch(arr, right, key) {
    let left = 0;
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (arr[mid] >= key) right = mid - 1;
        else left = mid + 1;
    }
    return left;
}
编辑于 2021-03-25 11:40:59 回复(0)
  1. 定义动态数组,int[] dp = new int[N];并全部赋初值为1,Arrays.fill(dp,1),dp代表nums 前 i 个数字的最长子序列长度
  2. 定义TreeSet,一种有序队列。并将数组首位加入到set
  3. 当数组元素大于set的最后末尾元素时,加入set,dp[i] = set.size()。否则就用ceiling(arr[i])找到大于当前元素的值,并使用remove删除,并将当前元素加入set,dp[i] = set.headSet(arr[i]).size() + 1(因为可能存在连续加入set的情况,这样遇到arr[i] < set.last()时,就只能删除一个set.celling(),此时,用set.size()就不对了)
  4. 定义一个res,长度是set.size();
  5. 从后向前遍历,如果dp[i] == j,res[--j] = arr[i]。j表示set.size();
发表于 2021-09-09 22:36:29 回复(0)

问题信息

难度:
118条回答 14909浏览

热门推荐

通过挑战的用户

查看代码