最长上升子序列

题目描述

给定一个无序的整数数组,找到其中最长上升子序列的长度。

样例

输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

题解

方法1:

  • 遍历数组中的每一个元素,dp[i]存储以第i个元素作为子序列的最后一个字符时,0~i中最大递增的子序列的长度
  • 在0~i中,任何小于nums[i]的元素都可以作为子序列倒数第二个位置上的数,选择一个使该数结尾可以使最终递增子序列长度最大的元素 dp[i]=max{dp[j]+1,(0≤j<i,nums[j]<nums[i])}
  • 如果0~i中没有一个数小于nums[i],那么dp[i]=1,说明以nums[i]结尾的递增子序列只有它本身
  • 该方法的时间复杂度为O(N2
public int lengthOfLIS(int[] nums) {
        if(nums==null||nums.length==0)
        	return 0;
        int max=0;
        //dp[i]表示在以nums[i]结尾的情况下,0~i范围中最长递增的子序列的长度
        //在0~i中任何小于nums[i]的数都可以作为倒数第二个数,目标应该是选择一个使其以i结尾的递增子序列长度最长
        int dp[]=new int[nums.length];
        for(int i=0;i<nums.length;i++) {
        	dp[i]=1;
        	for(int j=0;j<i;j++) {
        		if(nums[i]>nums[j])
        			dp[i]=Math.max(dp[i], dp[j]+1);
        	}
        	max=Math.max(max, dp[i]);
        }
        return max;
    }

方法2:

  • 声明一个长度为N的数组end,end[i]存储了在所有长度为i+1的递增子序列中,最小结尾的数,初始end[0]=nums[0]
  • 变量right表示end的有效区域边界,在遍历元素的过程中,判断end的有效区域中是否存在一个元素大于num[i],若存在则更新该位置的值,置为nums[i],因为要保存每个长度下最小结尾的数;若不存在则有效区域向右扩一个位置,即递增子序列的长度+1;
  • 在end的有效区域中查找时采用二分查找,因为有效区域中的元素均是递增排列的。
  • 所以该方法的时间复杂为O(NlogN)
  • 最终最大递增子序列的长度记为right+1
//end[i]存储了在所有长度为i+1的递增子序列中,最小的结尾的数
	//当遍历第i个元素时在end的有效区内查找(使用二分查找的方式),是否存在一个元素大于等于nums[i]
	//若不存在则有效区向右扩一个,即递增子序列的长度也加1
	public int lengthOfLIS1(int[] nums) {
		if(nums==null||nums.length==0)
			return 0;
		int[]end=new int[nums.length];
		end[0]=nums[0];
		int right=0;
		for(int i=1;i<nums.length;i++) {
			int l=0,r=right;
			while(l<=r) {
				int mid=l+(r-l)/2;
				if(nums[i]>end[mid])
					l=mid+1;
				else
					r=mid-1;
			}
			right=Math.max(right, l);
			end[l]=nums[i];
		}
		return right+1;
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值