目录
题目链接:2918. 数组的最小相等和 - 力扣(LeetCode)
题目链接:2918. 数组的最小相等和 - 力扣(LeetCode)
注:下述题目描述和示例均来自力扣
题目描述
给你两个由正整数和 0
组成的数组 nums1
和 nums2
。
你必须将两个数组中的 所有 0
替换为 严格 正整数,并且满足两个数组中所有元素的和 相等 。
返回 最小 相等和 ,如果无法使两数组相等,则返回 -1
。
示例 1:
输入:nums1 = [3,2,0,1,0], nums2 = [6,5,0] 输出:12 解释:可以按下述方式替换数组中的 0 : - 用 2 和 4 替换 nums1 中的两个 0 。得到 nums1 = [3,2,2,1,4] 。 - 用 1 替换 nums2 中的一个 0 。得到 nums2 = [6,5,1] 。 两个数组的元素和相等,都等于 12 。可以证明这是可以获得的最小相等和。
示例 2:
输入:nums1 = [2,0,2,0], nums2 = [1,4] 输出:-1 解释:无法使两个数组的和相等。
提示:
1 <= nums1.length, nums2.length <= 10^5
0 <= nums1[i], nums2[i] <= 10^6
解法一:
解决思路
-
计算非零元素和零的数量
- 遍历两个数组,分别计算每个数组中非零元素的总和(
sum1
和sum2
)以及零的数量(zero1
和zero2
)。
- 遍历两个数组,分别计算每个数组中非零元素的总和(
-
处理特殊情况
- 情况1:两个数组都没有零
- 直接比较两个数组的非零总和是否相等。若相等,返回该总和;否则返回
-1
。
- 直接比较两个数组的非零总和是否相等。若相等,返回该总和;否则返回
- 情况2:其中一个数组没有零
- 例如,
nums1
无零,则nums2
的总和必须调整到与sum1
相等。此时需要满足:sum1 ≥ sum2 + zero2
(因为nums2
的最小总和为sum2 + zero2
,每个零替换为1)。
- 若满足条件,返回
sum1
;否则返回-1
。
- 例如,
- 情况1:两个数组都没有零
-
处理一般情况(两个数组都有零)
- 计算每个数组的最小可能总和:
nums1
的最小总和:sum1 + zero1
(每个零替换为1)。nums2
的最小总和:sum2 + zero2
(每个零替换为1)。
- 最终的总和必须至少是这两个值的较大者,因为较小的总和无法通过调整零的替换值来匹配较大的总和。
- 计算每个数组的最小可能总和:
Java写法:
public class Solution {
public long minSum(int[] nums1, int[] nums2) {
long sum1 = 0;
int zero1 = 0;
for (int num : nums1) {
if (num == 0) {
zero1++;
} else {
sum1 += num;
}
}
long sum2 = 0;
int zero2 = 0;
for (int num : nums2) {
if (num == 0) {
zero2++;
} else {
sum2 += num;
}
}
// 处理两个数组都没有0的情况
if (zero1 == 0 && zero2 == 0) {
return sum1 == sum2 ? sum1 : -1;
}
// 处理nums1没有0的情况
if (zero1 == 0) {
// sum1必须 >= sum2 + zero2,因为sum2的最小总和是sum2 + zero2
if (sum1 >= sum2 + zero2) {
return sum1;
} else {
return -1;
}
}
// 处理nums2没有0的情况
if (zero2 == 0) {
if (sum2 >= sum1 + zero1) {
return sum2;
} else {
return -1;
}
}
// 其他情况,计算两者的最小可能总和的最大值
long s1 = sum1 + zero1;
long s2 = sum2 + zero2;
return Math.max(s1, s2);
}
}
C++写法:
#include <vector>
#include <algorithm> // 用于max函数
using namespace std;
class Solution {
public:
long long minSum(vector<int>& nums1, vector<int>& nums2) {
long long sum1 = 0, sum2 = 0;
int zero1 = 0, zero2 = 0;
// 计算nums1的非零总和和零的数量
for (int num : nums1) {
if (num == 0) zero1++;
else sum1 += num;
}
// 计算nums2的非零总和和零的数量
for (int num : nums2) {
if (num == 0) zero2++;
else sum2 += num;
}
// 情况1:两个数组都没有零
if (zero1 == 0 && zero2 == 0) {
return (sum1 == sum2) ? sum1 : -1;
}
// 情况2:nums1没有零,nums2必须调整到sum1的总和
if (zero1 == 0) {
if (sum1 >= sum2 + zero2) return sum1;
else return -1;
}
// 情况3:nums2没有零,nums1必须调整到sum2的总和
if (zero2 == 0) {
if (sum2 >= sum1 + zero1) return sum2;
else return -1;
}
// 情况4:两个数组都有零,取最小总和的较大者
long long minSum1 = sum1 + zero1;
long long minSum2 = sum2 + zero2;
return max(minSum1, minSum2);
}
};
运行时间
时间复杂度和空间复杂度
复杂度分析:
- 时间复杂度:
O(n + m)
,其中n
和m
是两个数组的长度。需要遍历两个数组各一次,统计非零元素的和与零的数量。 - 空间复杂度:
O(1)
,仅用常数空间存储变量(如sum1
,sum2
,zero1
,zero2
)。
解释:
算法仅需遍历数组两次(统计非零和零),后续所有操作均为常数时间比较和计算,因此时间复杂度为线性级,且无需额外空间。
总结
- 零的最小替换值为1,因此每个数组的最小总和为
非零和 + 零的数量
。 - 当两数组均有零时,最终总和由较大的最小总和决定。
- 单边无零时,必须满足对方数组的最小总和不超过无零数组的总和。