📌  相关文章
📜  数组中最长子序列的长度,所有元素都为裸数字(1)

📅  最后修改于: 2023-12-03 15:26:11.364000             🧑  作者: Mango

数组中最长子序列的长度

介绍

在一个数组中,子序列是指将该数组任意个元素删除后得到的序列。那么最长子序列即是指该数组中连续且顺序不变的一段子序列中含有元素最多的那个子序列。

例如,对于数组 [1, 3, 5, 4, 7] 来说,其最长子序列为 [3, 5, 7],长度为 3

本文将介绍两种常见的用于计算数组最长子序列长度的算法:动态规划算法和贪心算法。

动态规划算法

这是最常用的解决最长子序列问题的算法。动态规划算法的基本思想是将问题分解为若干个子问题,然后求解子问题的最优解,最后推导出整个问题的最优解。具体来说,对于数组 nums,设 $f(i)$ 表示包含第 $i$ 个元素的最长子序列的长度,则有:

$$ f(i) = \max_{0\le j<i, nums[j]<nums[i]} f(j) + 1 $$

其中,$\max$ 表示从所有满足条件 $nums[j]<nums[i]$ 的 $j$ 中取最大的 $f(j)+1$。在实现时,可以使用一个数组 $dp$ 来存储上述状态,然后从左到右遍历数组,更新每一个状态的最优解,最后遍历整个 $dp$ 数组,找到最大的 $f(i)$ 即可。

以下是使用 Python 实现的动态规划算法:

def lengthOfLIS(nums):
    n = len(nums)
    dp = [1] * n
    for i in range(n):
        for j in range(i):
            if nums[j] < nums[i]:
                dp[i] = max(dp[i], dp[j] + 1)
    return max(dp)

该函数接受一个数组 nums 作为输入,返回该数组的最长子序列的长度。可以通过调用该函数,传入相应的数组参数进行测试,例如:

print(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]))  # 4
print(lengthOfLIS([1, 3, 5, 4, 7]))  # 3
贪心算法

与动态规划算法不同,贪心算法没有全局地求解问题的最优解,而是基于某种局部最优决策,逐步构建全局最优解。针对本题,可以使用一种名为“patience sorting”的贪心算法,它的基本思路是将每个元素看作一张扑克牌,然后按照一定规则将它们分成若干堆,使其称为一个“纸牌游戏”的过程。其中,第 $i$ 堆的顶部元素为 $h_i$,则有:

  1. 如果 $h_i$ 大于待插入的元素 $num$,则将其放在第 $i$ 堆的顶部;
  2. 否则,在已有堆中选择一个顶部元素 $h_j$ 满足 $h_j$ 小于 $num$,将其替换为 $num$。

这样最后按照上述规则生成的不同堆数量即为数组最长子序列的长度。

以下是使用 Python 实现的贪心算法:

import bisect

def lengthOfLIS(nums):
    n = len(nums)
    piles, top = [0] * n, 0
    for i in range(n):
        num = nums[i]
        j = bisect.bisect_left(piles, num, 0, top)
        piles[j] = num
        if j == top:
            top += 1
    return top

该函数同样接受一个数组 nums 作为输入,返回该数组的最长子序列的长度。可以通过调用该函数,传入相应的数组参数进行测试,例如:

print(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]))  # 4
print(lengthOfLIS([1, 3, 5, 4, 7]))  # 3
总结

本文介绍了两种常见的用于计算数组最长子序列长度的算法:动态规划算法和贪心算法。两种算法均可在时间复杂度为 $O(n^2)$ 和 $O(n \log n)$ 的范围内求解问题,但贪心算法在实际运行中更加高效。在实际编码中,可以根据具体需求选择适合的算法进行实现。