最长上升子序列(LIS)

public class LIS {

    public static int lengthOfLIS(int[] nums) {
        /**
         * @discribe O(n ²)复杂度:dp[i]=max(dp[j]+1 if(j<i and nums[i]>nums[j]))
         * @Params [nums]
         * @Retuern int
         */
        if (nums.length == 0) {
            return 0;
        }
        int[] dp = new int[nums.length];
        int res = 1;
        Arrays.fill(dp, 1);
        for (int i = 1; i < dp.length; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }

    public static int lengthOfLIS2(int[] nums) {
        /**
         * @discribe O(nlogn)复杂度, 维护一个数组tails[i]
         *  tails[i]为长度为 i+1 的子序列中末尾数最小的值.
         * 状态定义:tails[k] 的值代表 长度为 k+1子序列 的尾部元素值.
         * 转移方程:设 res为 tails 当前长度,代表直到当前的最长上升子序列长度.
         * 设 j∈[0,res),考虑每轮遍历 nums[k] 时,通过二分法遍历 [0,res) 列表区间,找出 nums[k] 的大小分界点,会出现两种情况:
         *
         * 区间中存在 tails[i]>nums[k]:将第一个满足 tails[i] > nums[k]执行tails[i]=nums[k] ;因为更小的 nums[k] 后更可能接一个比它大的数字(前面分析过).
         * 区间中不存在 tails[i]>nums[k]:意味着 nums[k]可以接在前面所有长度的子序列之后,因此肯定是接到最长的后面(长度为 res ),新子序列长度为 res + 1.
         * 初始状态:令 tails列表所有值 =0.
         * 返回值:res,即最长上升子子序列长度。
         *@Params [nums]
         * @Retuern int
         */
        if (nums.length < 2) {
            return nums.length;
        }
        int[] tails = new int[nums.length];
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            int left = 0, right = res;
            while (left < right) {
                int mid = left + (right-left) / 2;
                if (tails[mid] < nums[i]) {
                    left=mid+1;
                } else {
                    right=mid;
                }
            }
            tails[left] = nums[i];
            if (res == right) {
                res++;
            }
        }
        return res;
    }

    public static void main(String[] args) {
        int i = lengthOfLIS2(new int[]{2,2});
        System.out.println(i);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值