Java 中的最长和谐子序列

2024年9月10日 | 阅读10分钟

给出了一个包含 n 个整数的数组。任务是找到数组的最长和谐子序列的大小。如果子序列中的最大值元素和最小值元素之间的差值为 1,则该子序列称为和谐子序列。

示例 1

输入

int inArr1[] = {11, 12, 12, 13, 14, 15, 11, 11, 11, 11}

输出

7

解释

最长的和谐子序列是 {11, 12, 12, 11, 11, 11, 11},其大小为 7。因此,输出为 7。

示例 2

输入

int inArr1[] = {0, 1, 5, 8, 10, 19, 15, 25, 30, 40, 45, 7, 11}

输出

2

解释

最长的和谐子序列是 {0, 1} 或 {7, 8},以及 {10, 11}。每个和谐子序列的大小为 2。因此,输出为 2。

蛮力法:使用位掩码

概念是利用位掩码技术生成所有可能的子序列。对于每个子序列,检查它是否是和谐子序列。找出所有可能的有效子序列中的最大长度。

观察以下步骤

步骤 1: 定义一个变量 ans。将其初始化为 0。变量 "ans" 将包含最终答案。

步骤 2: 迭代 'j' 从 0 到 '2len - 1',其中 'len' 是输入数组 'inputArr[]' 的长度。

  • 初始化三个整数 'minVal'、'maxVal' 和 'currLen' 分别为 INT_MAX、INT_MIN 和 0,用于分别存储当前子序列的最小值、最大值和当前子序列的长度。
  • 迭代 'k' 从 0 到 'len - 1'
    • 如果 j 的 kth 位被设置,并且 'MIN_VALUE' 小于 inputArr[k],则更新 'MIN_VALUE' 为 inputArr[k]。
    • 如果 j 的 kth 位被设置,并且 'MAX_VALUE' 大于 inputArr[k],则更新 'MAX_VALUE' 为 inputArr[k]。
    • 增加 'currLen',因为我们正在考虑子序列中的当前元素。
  • 如果 'MIN_VALUE' 和 'MAX_VALUE' 之间的差值为 1,那么它是一个有效的子序列。因此,将 'ans' 更新为 'ans' 和 'currLen' 中的最大值。

步骤 3: 返回答案 'ans'。

现在,让我们观察上述算法的实现。

文件名: LongestHarmoniousSubsequence.java

输出

For the input array: 
11 12 12 13 14 15 11 11 11 11 
The length of the longest Harmonic subsequence is: 7


For the input array: 
0 1 5 8 10 19 15 25 30 40 45 7 11 
The length of the longest Harmonic subsequence is: 2

复杂度分析: 程序生成 0 到 2len - 1 的所有二进制值。之后,程序遍历数组。因此,程序的整体时间复杂度为 O(len x 2len),其中 'len' 是输入数组的长度。此外,程序不使用额外的空间,因此空间复杂度为 O(1)。

程序的 time complexity 清楚地表明它不适用于较大的输入。因此,需要进行一些优化来降低 time complexity。

优化

概念是枢轴化数组的每个元素,然后只取那些相等或差值为 1 的元素,使得子序列中的最大差值为 1。

观察以下步骤。

步骤 1: 定义一个变量 ans。将其初始化为 0。变量 "ans" 将包含最终答案。

步骤 2: 迭代 'j' 从 0 到 'len - 1'

  • 初始化 'currLen' 为 0。变量 'currLen' 用于保存当前子序列的长度。
  • 定义一个布尔标志 'flg' 并将其赋值为 false,如果有可能构成和谐子序列,则该值为 true。
  • 迭代 'k' 从 0 到 'len - 1'
    • 如果 kth 索引处的元素与 jth 索引处的元素相同,则将 'currLen' 增加 1。
    • 如果 kth 索引处的元素比 jth 索引处的元素大 1,则将 'currLen' 增加 1,并将标志 'flg' 设置为 true,因为当前子序列的最大差值为 1。
  • 如果标志 'flg' 为 true,则将答案 'ans' 更新为 'ans' 和 'currLen' 中的最大值。

步骤 3: 返回 'ans' 作为最终答案。

现在,让我们观察上述算法的实现。

文件名: LongestHarmonioussubsequence1.java

输出

For the input array: 
11 12 12 13 14 15 11 11 11 11 
The length of the longest Harmonic subsequence is: 7


For the input array: 
0 1 5 8 10 19 15 25 30 40 45 7 11 
The length of the longest Harmonic subsequence is: 2

复杂度分析: 在程序中,每个输入数组元素都会被枢轴化。此外,一个循环会遍历数组,需要 O(len) 的时间。因此,程序的整体时间复杂度为 O(len2),其中 'len' 是输入数组的大小。程序的空间复杂度是常数,即 O(1)。

另一种方法:使用 HashMap

该方法是在 HashMap 中存储一个元素出现的次数。在每次迭代中,检查两件事

  • 检查 inputArr[j] + 1 是否在 HashMap 中。如果存在,则当前子序列的长度将是 inputArr[j] 的总出现次数加上 (inputArr[j] + 1) 的总出现次数。
  • 检查 inputArr[j] - 1 是否在 HashMap 中。如果存在,则当前子序列的长度将是 inputArr[j] 的出现次数加上 (inputArr[j] - 1) 的总出现次数。

步骤如下

步骤 1: 定义一个变量 ans。将其初始化为 0。变量 "ans" 将包含最终答案。

步骤 2: 定义一个 HashMap 'freqHashMap' 来存储输入数组 inputArr[] 中每个元素出现的次数。

步骤 3: 迭代 'j' 从 0 到 'len - 1'

  • 在 HashMap 中增加 inputArr[j] 的计数。
  • 如果 inputArr[j] + 1 存在于 HashMap 中,即 freqHashMap[inputArr[j] + 1] > 0,则将 'ans' 更新为 'ans' 和 (freqHashMap [inputArr[j]] + freqHashMap[inputArr[j] + 1]) 中的最大值。
  • 如果 inputArr[j] - 1 存在于 HashMap 中,即 freqHashMap[inputArr[j] - 1] > 0,则将 'ans' 更新为 'ans' 和 (freqHashMap[inputArr[j]] + freqHashMap[inputArr[j] - 1]) 中的最大值。

步骤 4: 返回 'ans'。

以下程序实现了上述步骤。

文件名: LongestHarmonioussubsequence2.java

输出

For the input array: 
11 12 12 13 14 15 11 11 11 11 
The length of the longest Harmonic subsequence is: 7


For the input array: 
0 1 5 8 10 19 15 25 30 40 45 7 11 
The length of the longest Harmonic subsequence is: 2

复杂度分析: 在程序中,使用一个循环遍历输入数组,并将每个元素的计数增加到 HashMap 中,其摊销时间复杂度为 O(1)。因此,程序的整体时间复杂度为 O(N)。此外,HashMap 最多可以增长到输入数组的大小。因此,程序的整体空间复杂度也为 O(N)。其中 N 是输入数组中的总元素数量。