PHP 求数组中最长递增子序列

3次阅读

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

PHP 求数组中最长递增子序列

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 警告

text=ZqhQzanResources