Hard
题目描述
给你一个整数 n,表示一个你正试图恢复的未知数组的长度。同时给你一个数组 sums,其中包含未知数组所有 2^n 个子集和的值(顺序任意)。
返回长度为 n 的数组 ans,表示未知数组。如果存在多个答案,返回其中任意一个。
如果数组 sub 可以通过删除数组 arr 的某些(可能是零个或全部)元素得到,那么数组 sub 就是数组 arr 的一个子集。sub 中元素的和是 arr 的一个可能的子集和。空数组的和被认为是 0。
注意:测试用例保证至少存在一个正确答案。
示例 1:
输入:n = 3, sums = [-3,-2,-1,0,0,1,2,3]
输出:[1,2,-3]
解释:[1,2,-3] 能够生成给定的子集和:
- []:和为 0
- [1]:和为 1
- [2]:和为 2
- [1,2]:和为 3
- [-3]:和为 -3
- [1,-3]:和为 -2
- [2,-3]:和为 -1
- [1,2,-3]:和为 0
示例 2:
输入:n = 2, sums = [0,0,0,0]
输出:[0,0]
解释:唯一正确答案是 [0,0]。
示例 3:
输入:n = 4, sums = [0,0,5,5,4,-1,4,9,9,-1,4,3,4,8,3,8]
输出:[0,-1,4,5]
解释:[0,-1,4,5] 能够生成给定的子集和。
约束条件:
1 <= n <= 15sums.length == 2^n-10^4 <= sums[i] <= 10^4
解题思路
这道题的核心思想是递归分治。通过观察可以发现,如果我们知道原数组的一个元素 x,那么所有子集和可以分为两组:包含 x 的子集和与不包含 x 的子集和。
解题思路:
排序处理:首先对
sums排序,最小值一定是所有负数的和(如果有的话),最大值一定是所有正数的和。确定差值:第二小的值与最小值的差,就是原数组中绝对值最小的非零元素(如果存在)。
分组递归:假设当前要确定的元素是
diff,我们将当前的子集和分为两组:- 一组包含这个元素
diff - 一组不包含这个元素
包含
diff的子集和减去diff后,应该与不包含diff的子集和形成相同的多重集合。- 一组包含这个元素
验证选择:通过贪心匹配验证我们的选择是否正确。如果能完美匹配,说明我们找到了正确的元素。
递归求解:移除确定的元素后,对剩余的子集和递归求解。
这个算法的时间复杂度主要取决于每层递归中的匹配过程,空间复杂度为递归深度。由于 n ≤ 15,这个方法是可行的。
代码实现
class Solution {
public:
vector<int> recoverArray(int n, vector<int>& sums) {
if (n == 1) return {sums[1] - sums[0]};
sort(sums.begin(), sums.end());
int diff = sums[1] - sums[0];
multiset<int> with, without;
for (int sum : sums) {
with.insert(sum);
}
for (int sum : sums) {
if (with.count(sum) && with.count(sum + diff)) {
without.insert(sum);
with.erase(with.find(sum));
with.erase(with.find(sum + diff));
}
}
vector<int> withoutVec(without.begin(), without.end());
vector<int> result = recoverArray(n - 1, withoutVec);
// 验证选择正确性
multiset<int> expected;
for (int sum : without) {
expected.insert(sum);
expected.insert(sum + diff);
}
multiset<int> actual(sums.begin(), sums.end());
if (expected == actual) {
result.push_back(diff);
} else {
result.push_back(-diff);
}
return result;
}
};
class Solution:
def recoverArray(self, n: int, sums: List[int]) -> List[int]:
if n == 1:
return [sums[1] - sums[0]]
sums.sort()
diff = sums[1] - sums[0]
with_count = Counter(sums)
without = []
for sum_val in sums:
if with_count[sum_val] > 0 and with_count[sum_val + diff] > 0:
without.append(sum_val)
with_count[sum_val] -= 1
with_count[sum_val + diff] -= 1
result = self.recoverArray(n - 1, without)
# 验证选择正确性
expected = Counter()
for sum_val in without:
expected[sum_val] += 1
expected[sum_val + diff] += 1
actual = Counter(sums)
if expected == actual:
result.append(diff)
else:
result.append(-diff)
return result
public class Solution {
public int[] RecoverArray(int n, int[] sums) {
if (n == 1) return new int[] { sums[1] - sums[0] };
Array.Sort(sums);
int diff = sums[1] - sums[0];
var withCount = new Dictionary<int, int>();
foreach (int sum in sums) {
withCount[sum] = withCount.GetValueOrDefault(sum, 0) + 1;
}
var without = new List<int>();
foreach (int sum in sums) {
if (withCount.GetValueOrDefault(sum, 0) > 0 &&
withCount.GetValueOrDefault(sum + diff, 0) > 0) {
without.Add(sum);
withCount[sum]--;
withCount[sum + diff]--;
}
}
var result = new List<int>(RecoverArray(n - 1, without.ToArray()));
// 验证选择正确性
var expected = new Dictionary<int, int>();
foreach (int sum in without) {
expected[sum] = expected.GetValueOrDefault(sum, 0) + 1;
expected[sum + diff] = expected.GetValueOrDefault(sum + diff, 0) + 1;
}
var actual = new Dictionary<int, int>();
foreach (int sum in sums) {
actual[sum] = actual.GetValueOrDefault(sum, 0) + 1;
}
bool isValid = expected.Count == actual.Count &&
expected.All(kv => actual.GetValueOrDefault(kv.Key, 0) == kv.Value);
if (isValid) {
result.Add(diff);
} else {
result.Add(-diff);
}
return result.ToArray();
}
}
var recoverArray = function(n, sums) {
sums.sort((a, b) => a - b);
function solve(arr) {
if (arr.length === 1) return [];
const diff = arr[1] - arr[0];
const with0 = [];
const with1 = [];
const count = new Map();
for (const num of arr) {
count.set(num, (count.get(num) || 0) + 1);
}
for (const num of arr) {
if (count.get(num) > 0) {
with0.push(num);
count.set(num, count.get(num) - 1);
const target = num + diff;
if (count.get(target) > 0) {
with1.push(target);
count.set(target, count.get(target) - 1);
}
}
}
if (with0.includes(0)) {
return [diff].concat(solve(with0));
} else {
return [-diff].concat(solve(with1));
}
}
return solve(sums);
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n × 2^n) | 每层递归需要处理 2^(n-i) 个元素,共 n 层 |
| 空间复杂度 | O(2^n) | 递归栈深度为 n,每层存储子集和数组 |
相关题目
. Subsets (Medium)
. Subsets II (Medium)
. Recover the Original Array (Hard)