首页 > 试题广场 >

最长上升子序列(一)

[编程题]最长上升子序列(一)
  • 热度指数:61263 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度。
所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。
我们定义一个序列是 严格上升 的,当且仅当该序列不存在两个下标 ij 满足
数据范围:
要求:时间复杂度 O(nlogn), 空间复杂度 O(n)
示例1

输入

[6,3,1,5,2,3,7]

输出

4

说明

该数组最长上升子序列为 [1,2,3,7] ,长度为4
import java.util.*;
 
 
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        // write code here
        int len = arr.length;
        int[] dp = new int[len + 1];
        Arrays.fill(dp,Integer.MAX_VALUE);
        dp[0] = Integer.MIN_VALUE;
         
        for(int i = 0; i < len; i++){
            for(int j = i; j >= 0; j--){
                if(arr[i] > dp[j]){
                    dp[j + 1] = Math.min(dp[j + 1],arr[i]);
                }
            }
        }
        int res = 0;
        for(int i = 1; i <= len ; i++){
            if(dp[i] >= Integer.MAX_VALUE ){
                break;
            }else{
                res = i;
            }
        }
        return res;
    }
}

发表于 2022-05-12 17:46:43 回复(2)
经典动态规划求解
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        // write code here
        int n = arr.length;
        if(n == 0) return 0;
        int[] dp = new int[n];     // dp[i]表示以arr[i]结尾时的最长递增子序列长度
        Arrays.fill(dp, 1);
        int maxLen = 1;
        for(int i = 1; i < n; i++){
            for(int j = i - 1; j >= 0; j--){
                if(arr[j] < arr[i]){
                    // arr[j] < arr[i],可以把arr[i]接在arr[j]后面,构成长度为dp[j]+1的递增子序列
                    dp[i] = Math.max(dp[i], dp[j] + 1);     // 选择能构成更长递增子序列的arr[j]
                }
            }
            maxLen = Math.max(maxLen, dp[i]);
        }
        return maxLen;
    }
}

发表于 2021-12-11 11:44:22 回复(1)
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型vector 给定的数组
     * @return int整型
     */
    int LIS(vector<int>& arr) {
        // write code here
        vector<int>res;
        int len = arr.size();
        for(int i=0;i<len;i++){
            if(res.size() == 0 || res.back() < arr[i]){
                res.push_back(arr[i]);
            }else{
                int j = res.size()-2;
                while(j>=0){
                    if(arr[i] > res[j])break;
                    j--;
                }
                res[j+1] = arr[i];
            }
        }
        return res.size();
    }
};

发表于 2021-11-07 18:33:16 回复(0)
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型vector 给定的数组
     * @return int整型
     */
    int LIS(vector<int>& arr) {
        // write code here
        //dp[i]表示到当前位置的严格上升子序列长度
        if(arr.size()<1) return 0;
        vector<int> dp(arr.size(),1);
        for(int i=1;i<arr.size();i++){
            for(int j=0;j<i;j++){
                if(arr[j]<=arr[i]){
                    if(dp[j]+1>dp[i]){
                        dp[i] = dp[j]+1;
                    }
                }
            }  
        }
        sort(dp.begin(),dp.end());
        return dp[dp.size()-1];
    }
};
发表于 2023-04-03 15:06:10 回复(0)
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型vector 给定的数组
     * @return int整型
     */
    int LIS(vector<int>& arr) {
        // 时间复杂度O(N^2),空间复杂度O(N)
        if (arr.empty()) return 0;
        int res = 1;
        vector<int> dp(arr.size(), 1);
        for (int i = 1; i < arr.size(); ++i) {
            for (int j = 0; j < i; ++j) {
                if (arr[i] > arr[j]) {
                    dp[i] = max(dp[i], dp[j] + 1);
                    res = max(res, dp[i]);
                }
            }
        }
        return res;
    }
};

发表于 2022-11-08 14:45:10 回复(0)
class Solution:
    def LIS(self , arr: List[int]) -> int:
        res = 0
        n = len(arr)
        dp = [1 for i in range(n)]
        for i in range(1, n):
            for j in range(i):
                if arr[i] > arr[j] and dp[j]+1 > dp[i]:
                    dp[i] = dp[j]+1
                    res = max(res, dp[i])
        return res
发表于 2022-05-22 16:21:56 回复(0)
普通人脑溢血做法(非贪心算法)
 public int LIS(int[] arr) {
        // write code here

        int len = arr.length;

        if (len == 0)
            return 0;
        if (len == 1)
            return 1;

        // dp 表示截止到当前的上升子数组的最大值
        int[] dp = new int[len];
        dp[0] = 1;
        int maxLen = 1;
        for (int i = 1; i < len; i++) {
            // 每次开始的地方都要初始化为1
            dp[i] = 1;
            // 对 [j,i) 进行查找, dp[i] 记录其全局最大值
            /*
             * 因为  dp[i] = Math.max(dp[i], dp[j] + 1); 
             * 在计算的时候 j = 2 时,则可能是 dp[2] +1, 
             * 在 j=3 时,可能是 dp[3]+1, 而 dp[2] 和 dp[3] 的值在 i =2和 i=3 的时候已经计算过了,
             * 即使 arr[2] > arr[3], 我们在更新 i = 5 时的 dp[i] 的值时,也会取其较大值,
             * 因此整个遍历过程中,计算 dp[5]时,我们会在 dp[0~4] 之间找到一个最大值,
             * 并根据 arr[5] 的大小判断是否要 +1
             */
            for (int j = 0; j < i; j++) {
                // 找到一种,对于 dp[j] 来讲,多了一个元素

                if (arr[i] > arr[j]) {
                    // 此时的 dp[j] 早已在之前的循环中计算好了
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            maxLen = Math.max(maxLen, dp[i]);
        }

        return maxLen;
    }

大神做法:使用辅助数组,
  int n = arr.length;
        int[] res = new int[n];
        int end = -1;
        for(int i = 0; i < n; i++) {
            if(i == 0 || res[end] < arr[i]) {
                res[++end] = arr[i];
            }else {

                int index = binarySearch(res, end, arr[i]);
                // 更新数组的时候,只有最长的递增子串才可能突破 res 的长度
                // 例如: {25,26,27,1,2,3,4,5}, 到 arr[i] == 3 的时候就已经更新了 res[end], arr[i] == 4 时突破长度
                // 而 {25,26,27,5,4,3,2,1 } 就不会,因为 1 开始一直往 index[0] 更新,不会影响到整体长度
                res[index] = arr[i];
            }
        }
        return end + 1;
    }

    public int binarySearch(int[] arr, int end,int target) {
        int left = 0;
        int right = end;
        while(left < right) {
            int mid = (right + left) / 2;
            if(arr[mid] >= target)
                right = mid;
            else
                left = mid + 1;
        }
        return left;
    }



发表于 2024-04-07 15:44:42 回复(0)
#include <memory>
#include <vector>
class Solution {
  public:
    int LIS(vector<int>& arr) {
        vector<int>d(arr.size(),1);
        int ans=0;
        if(arr.size()==1||arr.size()==0){
            return arr.size();
        }
        for(int i=1;i<arr.size();i++){
            for(int j=0;j<i;j++){
                if(arr[i]>arr[j]&&d[i]<d[j]+1){
                    d[i]=d[j]+1;
                }
                ans=max(ans,d[i]);
            }
        }
        return ans;
    }
};

编辑于 2024-04-05 15:08:28 回复(0)
#define max(a,b) (a>b?a:b)
int LIS(int* arr, int arrLen ) {
    int dp[1000]={1}, res=1, i, j;
    if(arrLen==0)
        return 0;
    for(i=0; i<sizeof(dp)/sizeof(int); i++)
        dp[i] = 1;
    for(i=1; i<arrLen; i++) {
        for(j=0; j<i; j++) {
            if(arr[i]>arr[j]) {
                dp[i] = max(dp[i], dp[j]+1);
                res = max(res, dp[i]);
            }
        }
    }
    return res;    
}

编辑于 2024-03-24 19:06:41 回复(0)

在函数中,我们首先检查数组是否为空,如果为空,则返回0。然后,我们初始化一个dp数组,其长度与输入数组相同,并将所有元素初始化为1,因为每个元素本身可以看作是一个长度为1的子序列。

接下来,我们遍历输入数组,对于每个位置i,我们再遍历它之前的所有位置j,如果arr[i]大于arr[j],说明我们可以将arr[i]添加到以arr[j]结尾的子序列中,从而可能形成一个更长的子序列。因此,我们尝试更新dp[i]为dp[j] + 1(即以arr[j]结尾的子序列长度加1)。

最后,我们返回dp数组中的最大值,它代表整个数组的最长严格上升子序列的长度。

class Solution:
    def LIS(self , arr: List[int]) -> int:
        # write code here
        if not arr: return 0
        dp = [1]*len(arr)
        for i in range(len(arr)):
            for j in range(i):
                if arr[i] > arr[j]:
                    dp[i] = max(dp[i], dp[j]+1)
        return max(dp)
发表于 2024-03-16 11:33:12 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        // write code here
        if(arr.length == 1){
            return 1;
        }
        // 状态定义:dp[i]表示数组arr在第i个位置严格上升子序列的长度
        int dp[] = new int[arr.length];
        // 每个位置默认初始值为1
        Arrays.fill(dp,1);
        int res = 0;
        for(int i = 1;i < arr.length;i++){
            for(int j = 0;j < i;j++){
                // 状态转移
                if(arr[i] > arr[j]){
                    dp[i] = Math.max(dp[i],dp[j] + 1);
                }
                res = Math.max(dp[i],res);
            }
        }
        return res;
    }
}

编辑于 2024-02-07 13:33:13 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        // write code here
        // 固定,以i结尾的最长的长度 dp[i], 往前去找所有小于它的dp[j],找到后寻找出最大的
        int len = arr.length;
        if(len == 0) return 0;
        int[] dp = new int[len];
        for(int i = 0; i < len; i++) {
            dp[i] = 1;
        }
        int res = 1;
        for(int i = 1; i < len; i++) {
            for(int j = 0; j < i; j ++) {
                if(arr[j] < arr[i]) dp[i] = Math.max(dp[i], dp[j] + 1);
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

编辑于 2024-01-08 18:31:02 回复(0)
时间复杂度O(2^n)
#include <vector>
#include <unordered_map>
class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型vector 给定的数组
     * @return int整型
     */
    int ans = 0;
    int LIS(vector<int>& arr) {
        // write code here
        vector<int> res;
        f(arr,res,0);
        return ans;
    }
    void f(vector<int>& arr, vector<int>& res, int i) {
        if (i == arr.size()) {
            ans = max(ans, (int)res.size());
            return;
        }
        if (res.size()==0||arr[i] > res.back()) {
            res.push_back(arr[i]);
            f(arr, res, i + 1);
            res.pop_back();
            f(arr, res, i + 1);
        }else{
            f(arr,res,i+1);
        }
    }
    

};


发表于 2023-11-21 10:40:10 回复(0)
class Solution {
public:

    int LIS(vector<int>& arr) {
        if(arr.size()==0) return 0;
        if(arr.size() == 1) return 1;
        int ans = 1;

        // 表示以 i 位置结尾的最长上升子序列
        vector<int> dp(arr.size(), 1);  
        for(int i=1;i<arr.size(); ++i){
            for(int j = 0; j<i; ++j){
                // 以 j 元素结尾的子序列 + 末尾元素i 也是严格递增的
                // 所以转移方程为 dp[i] = dp[j] + 1
                if(arr[j] < arr[i]){
                    dp[i] = max(dp[i], dp[j] + 1);
                    ans = max(ans, dp[i]); // 找到最大长度
                }
            }
        }
        return ans;
    }
};

发表于 2023-10-27 17:49:43 回复(0)
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    func LIS ( _ nums: [Int]) -> Int {
        guard !nums.isEmpty else {
            return 0
        }

        // dp[i]表示0-i以第i个元素结尾的序列的最长上升子序列
        var dp: [Int] = Array(repeating: 1, count: nums.count)
        for i in 1 ..< nums.count {
            for j in 0 ..< i {
                if nums[j] < nums[i], dp[i] < dp[j] + 1 { // nums[i]和nums[j]构成上升的子序列且新的HLS大于以i结尾的HLS,即dp[i],更新值
                    dp[i] = dp[j] + 1
                }
            }
        }
        return dp.sorted(by: { $0 < $1 }).last!
    }
}

发表于 2023-08-17 22:16:31 回复(0)
class Solution:
    def LIS(self , arr: List[int]) -> int:
        if len(arr) == 0:
            return 0
        n = len(arr)
        dp = [1] * n
        maxVal = 0
        for i in range(n):
            for j in range(i):
                if arr[j] < arr[i]:
                    dp[i] = max(dp[i], 1 + dp[j])
            maxVal = max(maxVal, dp[i])

        return maxVal
发表于 2023-08-10 16:41:59 回复(0)
js实现代码如下:
function LIS(arr) {
    const n = arr.length;
    if (n === 0) return 0;
    const dp = new Array(n).fill(1);
    let max = 1;
    for (let i = 1; i < n; i++) {
        let j = i - 1;
        while (j >= 0 && j >= dp[i] - 1) {
            if (arr[j] < arr[i]) {
                dp[i] = Math.max(dp[j] + 1, dp[i]);
                console.log(i, dp[i]);
            }
            j--;
        }
        if (dp[i] > max) {
            max = dp[i];
        }
    }
    return max;
}


发表于 2023-08-08 10:53:49 回复(0)
#include "iostream"
#include "stdio.h"
#include <algorithm>
#include "vector"
using namespace std;
//问题描述:给出一个数字序列,求这个数字序列的一个严格递增子序列,且这个子序列长度最长
//法一:动态规划,本算法的时间复杂度是O(n^2),空间复杂度是O(n)
int LIS(vector<int> arr)
{
    int n=arr.size();
    if(n==0) return 0;
    vector<int> dp(n+1,1);//初值全部置为1,下面再解释
    for(int i=1;i<=n;i++)
    {
        //遍历arr[i]之前的每一个小于arr[i]的元素,比较
        for(int j=1;j<i;j++)
        {
            //状态转移方程:dp[i]=max(dp[j]+1,dp[i]),1<=j<i
            //需要注意的是,如果arr[i]之前的所有元素均不小于arr[i],那么arr[i]就要置为1表示前i个元素中的最长上升子序列就是arr[i]元素自己
            //因为前i个元素中最小的就是自己了,例如序列12314,如果dp[4]=3,那么dp[5]=dp[4]+1=5,显然不合理
            if(arr[j-1]<arr[i-1])//数组下标是从0开始的,所以要减一
            dp[i]=max(dp[i],dp[j]+1);
        }
    }

    //循环赵dp数组中最大的那一个
    int mx=-1;
    for(int i=1;i<=n;i++)
    {
        mx=max(mx,dp[i]);
    }
    return mx;
}
//法二:贪心,最坏时间复杂度还是O(N^2),但空间复杂度降低了一些(没有dp重复计算)

/*
算法原理:
1.创建一个空的辅助数组res,用于保存最长递增子序列。
2.遍历输入数组中的每个元素:
如果res为空或者当前元素大于res中的最后一个元素,将当前元素添加到res末尾,因为它可以延长最长递增子序列的长度。
否则,在res中寻找第一个小于当前元素的位置,并将该位置后面的元素替换为当前元素。这样做可以保持res中元素的有序性,并且能够在可能更小的元素出现时更新最长递增子序列。
3.返回res的长度作为最长递增子序列的长度。
*/


//此算法可以优化,在查找res数组中第一个小于arr[i]元素时,可以用二分查找,这样时间复杂度就变成O(nlogn)了
//此算法求得的长度一定是最优解,但是res这个数组不一定是正确的
int LIS2(vector<int>& arr) {
    vector<int>res;//保存最长递增子序列
    int len = arr.size();
    for(int i=0;i<len;i++)
    {
        //arr[i]比res尾更小,就直接把其接在后面
        if(res.size() == 0 || res.back() < arr[i])
        {
            res.push_back(arr[i]);
        }
        else//否则说明arr[i]比res尾部更小,那么把res中间的某个元素更新成arr[i],就有可能获得更长的递增序列
        {
            int j = res.size()-2;//找到res的倒数第二个元素,开始从后往前遍历
            //在res中找到第一个小于arr[i]的元素res[j],那么res[j+1]一定是第一个不小于arr[i]的元素,将其值替换为更小的arr[i]
            while(j>=0)
            {
                if(arr[i] > res[j])break;
                j--;
            }
            res[j+1] = arr[i];
        }
    }
    //验证输出
    for(int i=0;i<res.size();i++)
    {
        cout<<res[i];
    }
    cout<<endl;
    return res.size();
}
int main()
{
    vector<int> arr={2,3,4,1,4,5,2};
    cout<<LIS2(arr);

    return 0;
}

发表于 2023-08-02 16:41:14 回复(0)
public int LIS (int[] arr) {
        //上升子序列,不连续,需要判断大小
        int[] dp=new int[arr.length];
        Arrays.fill(dp,1);
        if(arr.length==1) return 1;
        int ans=0;
        for(int i=1;i<arr.length;i++){
            for(int j=0;j<i;j++){
                if(arr[j]<arr[i]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            ans=Math.max(ans,dp[i]);
        } 
        return ans;
    }

发表于 2023-07-22 11:05:58 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        int n =arr.length;
        if (n < 2) {
            return n;
        }
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int result = 1;
        for (int i = n - 2; i >= 0; i--) {
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] > arr[i]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            result = Math.max(result, dp[i]);
        }
        return result;
    }
}

发表于 2023-07-07 20:31:34 回复(0)

问题信息

难度:
61条回答 2345浏览

热门推荐

通过挑战的用户

查看代码