算法(动态规划)----最长上升子序列

最长上升子序列

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

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

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗? 

动态规划

  •  第 1 步:定义状态。首先考虑“题目问什么,就把什么定义成状态”,发现无从下手。但可以基于下面这个事实,考虑状态的定义:

为了从一个较短的上升子序列得到一个较长的上升子序列,我们主要关心这个较短的上升子序列结尾的元素。

  • 为了保证子序列的相对顺序性,在程序读到一个新的数的时候,如果比已经得到的子序列的最后一个数还大,那么就可以放在这个子序列的最后,形成一个更长的子序列。
  • 一个子序列一定会以一个数结尾,于是将状态定义成:dp[i] 表示以 nums[i] 结尾的“最长上升子序列”的长度,注意这个定义中 nums[i] 必须被选取,且必须被放在最后一个元素。
  • 第 2 步:考虑状态转移方程;
  • 遍历到 nums[i] 时,考虑把索引 i 之前的所有的数都看一遍,只要当前的数 nums[i] 严格大于之前的某个数,那么 nums[i] 就可以接在这个数后面形成一个更长的上升子序列。因此,dp[i] 就等于索引 i 之前严格小于 nums[i] 的状态最大者 +1+1。
  • 语言描述:在索引 i 之前严格小于 nums[i] 的所有状态中的最大者 + 1+1。
  • 符号描述:

                                               

  • 第 3 步:考虑初始化:dp[0] = 1,1 个字符当然也是长度为 1 的上升子序列;
  • 第 4 步:考虑输出:所有 dp[i] 中的最大值(dp[i] 考虑了所有以 nums[i] 结尾的上升子序列);
  • 第 5 步:考虑状态压缩:之前所有的状态都得保留,因此无法压缩。
  • 时间复杂度O(n^2)
  • 空间复杂度O(n)

代码:

public int lengthOfLIS(int[] nums) {
		if (nums.length < 2) {
			return nums.length;
		}
		int[] dp = new int[nums.length];
		Arrays.fill(dp, 1);
		for (int i = 1; i < nums.length; i++) {
			for (int j = 0; j < i; j++) {
				if (nums[j] < nums[i]) {
					dp[i] = Math.max(dp[i], dp[j] + 1);
				}
			}
		}
		int max = -1;
		for (int i = 0; i < nums.length; i++) {
			max = Math.max(max, dp[i]);
		}
		return max;
	}

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值