Longest Increasing Subsequence最长递增子序列(动态规划+二分查找)

本文介绍了一种求解最长递增子序列(LIS)问题的有效算法,通过动态规划结合二分查找实现了O(n log n)的时间复杂度,并提供了C++及Java实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

Given an unsorted array of integers, find the length of longest increasing 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 be more than one LIS combination, it is only necessary for you to return the length.

Your algorithm should run in O(n2) complexity.

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

同时这也是完美世界的校招面试题。

 

给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱) 
例如:给定一个长度为8的数组A{1,3,5,2,4,6,7,8},则其最长的单调递增子序列为{1,2,4,6,7,8},长度为6.

输入描述:

第一行包含一个整数T,代表测试数据组数。

对于每组测试数据: 
N-数组的长度

a1 a2 … an (需要计算的数组)

保证:

1<=N<=3000,0<=ai<=MAX_INT.

输出描述:

对于每组数据,输出一个整数,代表最长递增子序列的长度。

输入例子: 
6 4 8 2 17

输出例子: 

解法参考youtube视频:https://www.youtube.com/watch?v=1RpMc3fv0y4该视频给出了时间复杂度为nlogn的算法,

该算法主要是基于动态规划+二分查找 (但该视频说是基于耐心排序)

dynamic programe+binary search

该视频讲到这道程序的核心是:

append if greater  ,replace if smaller

并且给出了append和replace如何进行的整个过程。替换是根据二分查找替换

current>last:  append current if greater than the last element

current<=last: replace  element >=current

 

O(nlogn)解法: 
这个解法应该是改自耐心排序。同样借助一个辅助数组sub,这个数组里记录了最长的递增子序列,尽可能保证这里面的元素最小。从头遍历nums,先把nums中第一个元素放到sub中,然后开始遍历数组。如果sub中最后一个元素小于第i个元素,说明sub中有元素应该更新,用折半查找找到第一个比第i个元素小的,替换掉。如果大,那么直接加入到sub的尾部。最后返回sub的长度即可。

C++写法

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int *sub = new int[nums.size()];
        int length = 0;
        for(int i = 0;i<nums.size();i++)
        {
            if(i == 0) 
            {
                sub[length++] = nums[i];
            }
            else
            {
                if(sub[length-1] < nums.at(i))//直接插入在最后
                {
                    sub[length++] = nums.at(i);
                }
                else if(sub[length-1] > nums.at(i))//折半插入
                {
                    insert(sub, length, nums.at(i));
                }
            }


        }
        return length;
    }


    void insert(int *sub, int &length, int &value)
    {

        int l = 0;
        int h = length - 1;
        while(l<=h)
        {
            int mid = (l+h)/2;
            if(value > sub[mid]) l = mid+1;
            else h = mid -1;
        }
        sub[l] = value;
    }
};

java写法

 

package sort;

public class LongestSequence {

    public static int sort(int[] arr) {
        int[] nums = new int[arr.length];
        int numsLastindex = 0;
        nums[0] = arr[0];
        for (int i = 1; i < arr.length; i++) {
// append if greater
            if (nums[numsLastindex] < arr[i]) {
                nums[++numsLastindex] = arr[i];
            } else {
// replace if smaller
                int index = binarySearch(nums, numsLastindex, arr[i]);
                nums[index] = arr[i];
            }
        }
        return numsLastindex+1;
    }

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

    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 5, 2};
        System.out.println(sort(arr));
        /*int[] ints = sort(arr);
        for (int i = 0; i < ints.length; i++) {
            System.out.println(ints[i]);
        }*/
    }
}

值得注意的,基于这个方法求出的长度是正确的,但是求出的nums数组不一定是正确的。

例如,输入{1, 3, 5, 2},输出的数组为{1,2 5},记录的长度为3。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值