Medium
题目描述
给你一个长度为 n 的数组 nums 和一个整数 m。你需要判断是否可以通过一系列步骤将数组拆分成 n 个长度为 1 的数组。
一个数组被称为"好数组"当且仅当:
- 数组的长度为 1,或者
- 数组元素的和大于或等于
m
在每一步中,你可以选择一个现有的长度至少为 2 的数组(可能是之前步骤的结果),将其拆分为两个数组,前提是拆分后的两个数组都是好数组。
如果你能将给定数组拆分成 n 个数组,返回 true,否则返回 false。
示例 1:
输入:nums = [2, 2, 1], m = 4
输出:true
解释:
- 将 [2, 2, 1] 拆分为 [2, 2] 和 [1]。数组 [1] 长度为 1,数组 [2, 2] 元素和为 4 >= m,所以都是好数组。
- 将 [2, 2] 拆分为 [2] 和 [2]。两个数组长度都为 1,所以都是好数组。
示例 2:
输入:nums = [2, 1, 3], m = 5
输出:false
解释:
第一步必须是以下之一:
- 将 [2, 1, 3] 拆分为 [2, 1] 和 [3]。数组 [2, 1] 既不是长度为 1,元素和也不大于等于 m。
- 将 [2, 1, 3] 拆分为 [2] 和 [1, 3]。数组 [1, 3] 既不是长度为 1,元素和也不大于等于 m。
所以两种操作都无效,无法将 nums 拆分成 n 个长度为 1 的数组。
示例 3:
输入:nums = [2, 3, 3, 2, 3], m = 6
输出:true
约束:
1 <= n == nums.length <= 1001 <= nums[i] <= 1001 <= m <= 200
解题思路
这道题的关键在于理解拆分的规则和找到关键的观察点。
核心观察: 通过分析拆分过程,我们可以发现一个重要规律:当数组长度大于 2 时,能否成功拆分主要取决于是否存在相邻元素对的和 ≥ m。
思路分析:
- 如果数组长度为 1 或 2,总是可以拆分成功(长度为 1 直接满足条件,长度为 2 可以拆分成两个长度为 1 的数组)
- 对于长度大于 2 的数组,我们需要在某个位置进行拆分,使得拆分后的两个子数组都是"好数组"
- 关键观察:如果存在相邻的两个元素
nums[i] + nums[i+1] >= m,那么我们总是可以成功拆分 - 这是因为我们可以将这两个相邻元素单独拆分出来作为一个子数组(满足和 ≥ m 的条件),剩余部分递归处理
算法步骤:
- 特殊情况:如果数组长度 ≤ 2,直接返回
true - 遍历数组,检查是否存在相邻元素对的和 ≥ m
- 如果存在,返回
true;如果不存在,返回false
这个解法的正确性基于:当我们能够找到一个相邻对时,我们总能通过适当的拆分策略最终将数组完全拆分成单个元素。
代码实现
class Solution {
public:
bool canSplitArray(vector<int>& nums, int m) {
int n = nums.size();
// 长度为1或2的数组总是可以拆分
if (n <= 2) {
return true;
}
// 检查是否存在相邻元素对的和 >= m
for (int i = 0; i < n - 1; i++) {
if (nums[i] + nums[i + 1] >= m) {
return true;
}
}
return false;
}
};
class Solution:
def canSplitArray(self, nums: List[int], m: int) -> bool:
n = len(nums)
# 长度为1或2的数组总是可以拆分
if n <= 2:
return True
# 检查是否存在相邻元素对的和 >= m
for i in range(n - 1):
if nums[i] + nums[i + 1] >= m:
return True
return False
public class Solution {
public bool CanSplitArray(IList<int> nums, int m) {
int n = nums.Count;
// 长度为1或2的数组总是可以拆分
if (n <= 2) {
return true;
}
// 检查是否存在相邻元素对的和 >= m
for (int i = 0; i < n - 1; i++) {
if (nums[i] + nums[i + 1] >= m) {
return true;
}
}
return false;
}
}
var canSplitArray = function(nums, m) {
const n = nums.length;
// 长度为1或2的数组总是可以拆分
if (n <= 2) {
return true;
}
// 检查是否存在相邻元素对的和 >= m
for (let i = 0; i < n - 1; i++) {
if (nums[i] + nums[i + 1] >= m) {
return true;
}
}
return false;
};
复杂度分析
| 复杂度类型 | 值 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |
其中 n 为数组长度。算法只需要遍历一次数组检查相邻元素对,使用常数额外空间。