【每日一题】【二分mid&贪心】2022年2月8日-NC163 最长上升子序列(一)

1、描述
给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度。
所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。

2、介绍

最长递增子序列(longest increasing subsequence),简称LIS

3、方法1:贪心+二分

import java.util.*;

public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 给定数组的最长严格上升子序列的长度。
* @param arr int整型一维数组 给定的数组
* @return int整型
*/
public int LIS (int[] arr) {
int len = 1, n = arr.length;
if(n == 0) {
return 0;
}
int[] lisArr = new int[n + 1];
lisArr[len] = arr[0];
for(int i = 1; i < n; i++) {
//len代表lisArr中的最后一个元素
if(lisArr[len] < arr[i]) {
lisArr[++len] = arr[i];
} else {
//二分,在lisArr数组中找比arr[i]小的,最靠后的位置pos
//未找到,即lisArr数组中所有元素均大于arr[i]
//需要更新lisArr数组的位置,为何不设为1呢?
int l = 1,r = len, pos = 0;
while(l <= r) {
//int mid = l + (r - l) >> 1;是错误的,原因是优先级导致的
int mid = l + ((r - l) >> 1);//位运算是为了避免溢出
//int mid = (l + r) >> 1;
if(lisArr[mid] < arr[i]) {
//更新指针
l = mid + 1;
pos = mid;
} else {
r = mid - 1;
}
}
//循环结束,找到了pos
lisArr[pos + 1] = arr[i];
//pos是最后一个位置,len不变
//pos是中间位置,len仍不变,元素继续往后填
//所以没有必要更新len位置
}
}
return len;
}
}

4、二分使用库函数实现

import java.util.*;

public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 给定数组的最长严格上升子序列的长度。
* @param arr int整型一维数组 给定的数组
* @return int整型
*/
public int LIS (int[] arr) {
//使用二分查找确定插入位置
//贪心
int n = arr.length, len = 1;
if(n <= 1) {
return n;
}
int[] lisArr = new int[n + 1];
lisArr[len] = arr[0];
for(int i = 1; i < n; i++) {
if(lisArr[len] < arr[i]) {
lisArr[++len] = arr[i];
} else {
//从1开始
int res = Arrays.binarySearch(lisArr, 1, len + 1, arr[i]);
if(res >= 0) { //表示找到了插入位置
//即存在相同元素时,跳过
continue;
} else { //表示没有找到插入位置
//则res<0
//其值表示应插入位置的负数
int ins = -(res + 1);
lisArr[ins] = arr[i];
}
}
}
return len;
}
}

5、方法2:动态规划

import java.util.*;

public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 给定数组的最长严格上升子序列的长度。
* @param arr int整型一维数组 给定的数组
* @return int整型
*/
public int LIS (int[] arr) {
//动态规划实现
//最长子序列应当使用max值记录
int n = arr.length;
if(n == 0) {
return 0;
}
int[] dp = new int[n];
Arrays.fill(dp, 1);
int maxLen = Integer.MIN_VALUE;
for(int i = 0; i < n; i++) {
for(int j = 0; j < i; j++) {
if(arr[j] < arr[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
maxLen = Math.max(maxLen, dp[i]);
}
}
return maxLen;
}
}

【每日一题】【二分mid&贪心】2022年2月8日-NC163 最长上升子序列(一)的相关教程结束。

相关推荐:

如何根据前一个连续段的值动态修改数值序列

本文介绍如何在pandas中识别数值连续段(streak),并基于前一段的值条件性地修改当前段——例如,将紧随“1”段之后的所有“2”段统一替换为1。 本文介绍如何在pandas中识别数值连续段(streak),并基于前一段的值条件性地修改当前段——例如,将紧随“1”段之后的所有“2”段统一替换为1...

如何根据前一个连续段落的值修改当前数字序列

本文介绍如何在Pandas中识别数值的连续段(streak),并基于前一段的值批量修改当前段——例如,将紧跟在1段之后的所有2段统一替换为1,同时保持其他值(如-1)不变。 本文介绍如何在pandas中识别数值的连续段(streak),并基于前一段的值批量修改当前段——例如,将紧跟在`1`段之后的所...

如何用Python TensorFlow实现时间序列预测_通过LSTM窗口切片解决

直接用tf.keras.layers.LSTM预测时间序列输出全零或震荡严重,是因为默认return_sequences=False导致模型只输出末步状态,而时间序列预测需滑动窗口对齐输入与标签;解决关键是手动构造X_train和y_train,确保窗口不越界、维度正确、时序因果性不被破坏。 为什么...

如何使用Python的zip函数并行迭代多个序列_利用zip将其打包为元组对

zip是“最短截断”惰性迭代器,任一输入耗尽即停止,返回单次可用的zip对象;解包zip(*)可反向拆分,但结果为元组。 Python的zip函数不是“打包成元组对”,而是把多个可迭代对象的对应位置元素组合成元组,生成一个惰性迭代器;一旦某个输入耗尽,zip立即停止——这是它最常被误用也最容易出bu...

Python中Scikit-learn如何实现时间序列交叉验证_利用TimeSeriesSplit

TimeSeriesSplit不能直接套用普通交叉验证,因其会破坏时序依赖性;它强制训练集时间早于验证集,滑动增长训练窗口,不泄露未来信息,返回索引需手动切分数据。 TimeSeriesSplit为什么不能直接套用普通交叉验证 普通KFold或train_test_split会随机打乱样本顺序,破坏...

如何将 Pandas Series 中的嵌套 Series 扁平化为字符串序列

当使用str.extract()提取匹配内容时,若未正确设置参数,可能返回SeriesofSeries;只需将expand=False(默认值)显式指定,并确保正则表达式含捕获组,即可直接获得一维字符串Series。 当使用`str.extract()`提取匹配内容时,若未正确设置参数,可能返回se...