php中求最长递增子序列有两种主流方法:一是o(n²)动态规划,定义dp[i]为以i结尾的lis长度;二是o(n log n)二分优化法,维护tail数组并用二分查找更新。

PHP 中求最长递增子序列(Longest Increasing Subsequence, LIS)有多种实现方式,核心在于理解动态规划(DP)或二分优化的思路。下面给出两种实用、易懂且可直接运行的方案。
方法一:动态规划(O(n²),适合中小规模数组)
定义 dp[i] 表示以索引 i 结尾的最长递增子序列长度。遍历每个位置,向前检查所有比它小的元素,更新 dp 值。
示例代码:
function lengthOfLIS($nums) {
if (empty($nums)) return 0;
$n = count($nums);
$dp = array_fill(0, $n, 1); // 每个元素自身构成长度为1的子序列
for ($i = 1; $i < $n; $i++) {
for ($j = 0; $j < $i; $j++) {
if ($nums[$j] < $nums[$i]) {
$dp[$i] = max($dp[$i], $dp[$j] + 1);
}
}
}
return max($dp);
}
// 测试
$arr = [10, 9, 2, 5, 3, 7, 101, 18];
echo lengthOfLIS($arr); // 输出:4(对应 [2,3,7,18] 或 [2,5,7,101])
方法二:二分查找优化(O(n log n),推荐用于大数组)
维护一个辅助数组 tails,其中 tails[i] 存储长度为 i+1 的所有递增子序列中结尾最小的那个值。每次用二分查找定位插入位置,替换或追加元素。
立即学习“PHP免费学习笔记(深入)”;
该方法只返回长度,不直接给出子序列内容;如需还原路径,需额外记录前驱索引。
示例代码:
function lengthOfLISOptimized($nums) {
if (empty($nums)) return 0;
$tails = [];
foreach ($nums as $num) {
$left = 0;
$right = count($tails);
// 二分查找第一个 >= $num 的位置
while ($left < $right) {
$mid = (int)(($left + $right) / 2);
if ($tails[$mid] < $num) {
$left = $mid + 1;
} else {
$right = $mid;
}
}
$tails[$left] = $num;
}
return count($tails);
}
// 测试
$arr = [10, 9, 2, 5, 3, 7, 101, 18];
echo lengthOfLISOptimized($arr); // 输出:4
补充:获取实际的最长递增子序列(非仅长度)
若需返回具体子序列(如 [2, 3, 7, 18]),可在 DP 方法基础上增加 prev 数组记录路径:
- 在 DP 循环中,当
$dp[$j] + 1 > $dp[$i]时,同时记录$prev[$i] = $j - 找到最大值对应索引后,反向追溯
$prev构建结果数组 - 最后用
array_reverse()得到正序子序列
注意事项
- “递增”默认指严格递增(
),如需非严格(<code>),只需修改比较条件 - 输入为空数组或单元素时,函数应正确返回 0 或 1
- PHP 数组索引从 0 开始,注意边界处理,避免 undefined index 警告











