辛星Java动态规划教程第五篇:最长递增子序列(LIS),时间复杂度O(nlogn)

本文介绍了如何使用动态规划在Java中实现最长递增子序列(LIS)问题的O(nlogn)解法。通过维护一个动态数组,根据新遍历的数据与数组中元素的关系更新数组,最终得到最长递增子序列的长度。这种方法利用二分查找,实现了高效的时间复杂度。
摘要由CSDN通过智能技术生成

对于最长递增子序列的O(n^2)解法,前面已经介绍过了,相信大家都很好理解,那么接下来就要介绍时间复杂度为O(nlogn)的解法了。

我们还是首先来介绍一下思路:
我们保存一个数组arr,它的第i项表示可以组成长度为 i+1个自增子序列的最小的结尾的那个数,这个数组里面所存储的并不是原序列的最长递增子序列,它的顺序是错乱的。
因为我们这里只要最长递增子序列的长度,这个长度就是这个数组arr里面有效元素的个数。

比如对于{5, 2, 9, 4, 8, 6, 7, 3, 1}来说,
当遍历5的时候,如果我们要组成长度为1的递增子序列,它的最小值为5, 我们在数组中保存一个元素5, 即[5]
当遍历2的时候,它比5要小,我们发现要组成长度为1的递增子序列,最大值设置为2即可,我们在数组中保存一个元素,即[2]
当遍历9的时候,它比2要大,我们发现我们可以组成长度为2的递增子序列,长度为2时最小值为9,我们在数组中保存[2,9]
当遍历4的时候,它比2要大,比9要小,我们发现我们可以组成长度为2的递增子序列,第二个元素只要是4就可以了,所以我们调整这个数组为[2,4]
当遍历8的时候,它比4要大,所以我们可以组成长度为3的递增子序列,第三个元素为8,所以我们调整这个数组为[2,4,8]
同理,当我们遍历6的时候,这个数组被调整为[2,4,6]
当我们遍历7的时候,这个数组被调整为[2,4,6,7]
当我们遍历3的时候,这个数组被调整为[2,3,6,7]
当我们遍历1的时候,这个数组被调整为[1,3,6,7]
它的最大递增子序列为: 2, 4, 6, 7,可以看到,和数组arr中保存的1,3,6,7并不相同

我们调整的规则如下:
(1).如果新遍历的数据比元素的最后一个数据要大,那么在末尾加入
(2).如果新遍历的数据比元素的最后一个数据不大,那么就替换掉比它大的数中最小的那个

需要说明的是,这个数组所存储的并不是
数组的长度就是当前最长递增子序列的长度,当然这里使用动态数组可以最大限度的节省空间,不过它可能需要来回的扩容,但是对于数据量较大,但是结果数据较少时,扩容的成本还是值得的。

下面再举一个例子吧,对于序列{65, 22, 34, 99, 27, 25, 100, 33, 35, 200, 44, 57} 来说,其逐次的判定结果分别是:
// [65]
// [22]
// [22, 34]
// [22,34,99]
// [22, 27, 99]
// [22, 25,99]
// [22,25,99,100]
// [22, 25,33, 100]
// [22, 25,33, 35]
// [22, 25, 33, 35, 44]
// [22, 25, 33, 35, 44, 57]

由于在Java中可以使用ArrayList来作为动态数组使用,而且是Java自带的工具类,我们在这里直接使用它。
因为这里用到了二分查找,所以它的时间复杂度为O(nlogn),如下:

package com.mengzhidu.teach.algorithm.dp.demo.line;

import java.util.ArrayList;

/**
 
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值