最长递增子数组问题
1. 问题分析
1.1 题目描述
给定两个整数数组 和 ,每个数组的长度为 。我们可以通过从 和 中轮流选择一个元素加入到新数组 的末尾,从而构造出新的数组 。每次可以选择 或 的第一个元素,并将它加入到 数组的末尾,然后从原数组中删除该元素,直到两个数组都为空为止。
题目的目标是:在所有可能生成的 数组中,找到其中一个“最长的递增子数组”,该递增子数组需要满足相邻两个元素的差的绝对值为 1。我们的任务是输出这个最长的递增子数组的长度。
1.2 输入输出样例
-
示例 1:
- 输入: ,
- 输出:4
- 解释:一种可能的 数组为 ,其中最长递增子数组为 ,长度为4。
-
示例 2:
- 输入:,
- 输出:4
-
示例 3:
- 输入:,
- 输出:3
2. 解题思路
本题的难点在于如何有效地从两个数组中选择元素加入 ,以构造出一个满足条件的最长递增子数组。这需要在选择元素的过程中动态追踪递增子数组的长度。
观察题目可得,这个问题包含以下关键要素:
- 递增子数组:子数组需要是递增的,相邻元素的差必须为1。
- 双数组交替选择:我们需要交替选择 或 中的元素。
- 贪心策略:尽量以形成连续序列为优先,通过追踪当前形成的序列长度更新最优解。
接下来我们将逐步分析解决思路。
2.1 思路 1:穷举所有可能的 数组
最朴素的思路是枚举所有可能的 数组,然后在每一个 数组中找到最长的符合条件的递增子数组。这样可以保证找到最长的递增子数组,但计算复杂度极高。假设 ,共有 种组合,显然不具备现实意义。
2.2 思路 2:单数组内部的最长递增子序列
我们可以尝试分别在 和 内部寻找满足条件的最长递增子数组,记录它们的最大长度。这是因为最终构造的 数组的最长递增子数组一定包含了 或 中的一部分连续子数组。
具体步骤为:
- 对 和 数组分别进行遍历,每次遇到符合“相邻元素差为1”的元素,累加当前递增子数组的长度;
- 每当不满足条件时,更新全局的最长递增子数组长度,并重置开始新的子数组;
- 在完成遍历后,将两个数组中的最长递增子数组长度比较,更新全局最优解。
但是该方法未能考虑到 跨数组递增序列 的可能性。例如在 中形成部分递增序列,然后在 中继续形成的情况。因此,我们还需要考虑双数组交替组合的情况。
2.3 思路 3:双数组交替组合(双指针)
在思路2的基础上,我们可以进一步利用 双指针法 来优化计算过程,以构造出两个数组交替组合的最长递增子序列。
具体做法为:
- 单数组递增子序列计算:首先在 和 中分别计算它们内部的最长递增子数组长度;
- 交替组合:以 中的任意元素 或 中的任意元素 为起点,逐步构造从 开始的序列,随后在 中寻找符合条件的下一个元素,形成跨数组的递增序列。
- 动态更新:每次递增时记录当前递增子序列的长度,并更新全局最大长度。
这样的组合方法能在较优的时间内找到最优解。
3. 代码实现
以下为该思路的代码实现:
#include <bits/stdc++.h>
using namespace std;
// 函数:计算最大递增子数组的长度
int solution(vector<int> a, vector<int> b) {
int ans = 0; // 初始化答案,即最长递增子数组的长度
int asz = a.size(), bsz = b.size(); // 分别记录 a 和 b 的长度
// 步骤 1:在 a 和 b 中单独查找最长递增子数组的长度
for (int i = 0, res = 0, lst = -19999; i < asz; ++i) {
// 如果 a[i] 是递增的,更新当前递增子数组的长度
if (a[i] == lst + 1) lst += 1, res += 1;
else lst = a[i], res = 1;
ans = max(ans, res); // 更新全局最长子数组长度
}
for (int i = 0, res = 0, lst = -19999; i < bsz; ++i) {
if (b[i] == lst + 1) lst += 1, res += 1;
else lst = b[i], res = 1;
ans = max(ans, res);
}
// 步骤 2:以 a[i] 和 b[j] 为起点,交替查找最长递增子数组
for (int i = 0; i < asz; ++i) {
for (int j = 0; j < bsz; ++j) {
int as = i, bs = j, lst, res = 1;
// 从 a[i] 或 b[j] 开始形成递增序列
if (a[as] < b[bs]) {
lst = a[as], as += 1;
} else lst = b[bs], bs += 1;
// 交替选择 a 和 b 的元素,确保相邻差值为 1
while (as < asz || bs < bsz) {
if (as < asz && a[as] == lst + 1) {
res += 1, lst += 1, as += 1;
ans = max(ans, res);
} else if (bs < bsz && b[bs] == lst + 1) {
res += 1, lst += 1, bs += 1;
ans = max(ans, res);
} else break; // 无法继续构建递增序列时跳出循环
}
}
}
return ans;
}
int main() {
cout << (solution({5, 2, 4}, {7, 1, 3}) == 4) << endl;
cout << (solution({3, 6, 8, 1}, {2, 7, 9, 5}) == 4) << endl;
cout << (solution({10, 15, 12, 13, 11}, {9, 14, 16, 18, 17}) == 3) << endl;
return 0;
}
4. 代码分析
时间复杂度分析
- 单数组查找:分别对 和 进行一次线性遍历,时间复杂度为 。
- 双数组交替查找:通过双重循环枚举起点 并在每次查找中进行线性遍历,总体复杂度约为 。
综合来看,算法的整体时间复杂度为 ,在 较小时可以满足性能要求。
空间复杂度分析
本算法只使用了常量空间存储临时变量,空间复杂度为 。
5. 测试与结果分析
在多组测试样例下,代码能够正确输出预期结果,验证了算法的有效性。