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);
}
}
最长上升子序列(LIS)
最新推荐文章于 2021-03-08 22:47:06 发布