在算法面试和实际开发中,C#最长递增子序列(Longest Increasing Subsequence,简称LIS)是一个非常经典的问题。它不仅考察你对动态规划的理解,还涉及算法优化技巧。本文将带你从零开始,深入浅出地掌握LIS问题,并学习如何用C#动态规划高效解决它。

给定一个整数数组,例如 [10, 9, 2, 5, 3, 7, 101, 18],我们要找出其中最长的严格递增子序列(元素顺序不变,但不必连续)。在这个例子中,一个可能的LIS是 [2, 3, 7, 18],长度为4。
注意:子序列 ≠ 子数组。子序列可以跳过中间元素,只要保持原有顺序即可。
最直观的方法是使用动态规划。我们定义 dp[i] 表示以第 i 个元素结尾的最长递增子序列的长度。
状态转移方程:
dp[i] = max(dp[j] + 1) ,其中 j < i 且 nums[j] < nums[i]下面是 C# 实现代码:
public static int LengthOfLIS_O_N2(int[] nums){ if (nums == null || nums.Length == 0) return 0; int n = nums.Length; int[] dp = new int[n]; Array.Fill(dp, 1); // 每个元素自身构成长度为1的子序列 for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (nums[j] < nums[i]) { dp[i] = Math.Max(dp[i], dp[j] + 1); } } } return dp.Max();}这个方法的时间复杂度是 O(n²),空间复杂度是 O(n)。对于小规模数据足够,但当 n 很大时(比如 10⁵),就会超时。
为了提升性能,我们可以使用LIS算法优化技巧:维护一个“潜在递增序列”的数组 tails,其中 tails[k] 表示长度为 k+1 的递增子序列的最小末尾元素。
关键思想:越小的末尾元素,越有利于后续扩展更长的子序列。
我们遍历数组,对每个元素使用二分查找找到它在 tails 中的插入位置,并更新该位置的值。
C# 实现如下:
public static int LengthOfLIS_O_NlogN(int[] nums){ if (nums == null || nums.Length == 0) return 0; List<int> tails = new List<int>(); foreach (int num in nums) { // 使用二分查找找到第一个 >= num 的位置 int left = 0, right = tails.Count; while (left < right) { int mid = left + (right - left) / 2; if (tails[mid] < num) left = mid + 1; else right = mid; } // 如果 left == tails.Count,说明 num 比所有元素都大,追加 if (left == tails.Count) { tails.Add(num); } else { tails[left] = num; // 替换,保持最小末尾 } } return tails.Count;}这个版本的时间复杂度降到了 O(n log n),非常适合处理大规模数据。这也是面试中常考的最长上升子序列C#实现优化方案。
class Program{ static void Main() { int[] nums = {10, 9, 2, 5, 3, 7, 101, 18}; Console.WriteLine("LIS 长度 (O(n²)): " + LengthOfLIS_O_N2(nums)); // 输出: 4 Console.WriteLine("LIS 长度 (O(n log n)): " + LengthOfLIS_O_NlogN(nums)); // 输出: 4 }}通过本文,你已经掌握了:
无论你是准备面试,还是想提升算法能力,掌握LIS算法优化都是必备技能。赶快动手试试吧!
本文由主机测评网于2025-12-07发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://vpshk.cn/2025124342.html