Medium
题目描述
一个整数数组 original 被转换为双倍数组 changed,转换过程是将 original 中每个元素的值乘以 2 后添加到数组末尾,然后随机打乱结果数组。
给定一个数组 changed,如果它是一个双倍数组,请返回 original。如果 changed 不是双倍数组,则返回空数组。original 中的元素可以按任意顺序返回。
示例 1:
输入:changed = [1,3,4,2,6,8]
输出:[1,3,4]
解释:一个可能的原始数组是 [1,3,4]:
- 1 的两倍是 1 * 2 = 2
- 3 的两倍是 3 * 2 = 6
- 4 的两倍是 4 * 2 = 8
其他可能的原始数组有 [4,3,1] 或 [3,1,4]
示例 2:
输入:changed = [6,3,0,1]
输出:[]
解释:changed 不是双倍数组
示例 3:
输入:changed = [1]
输出:[]
解释:changed 不是双倍数组
约束条件:
1 <= changed.length <= 10^50 <= changed[i] <= 10^5
解题思路
解题思路
这道题的关键在于理解双倍数组的性质:如果一个数组是由原始数组转换而来的双倍数组,那么数组长度必须是偶数,且每个原始元素 x 都对应一个双倍元素 2x。
核心思路
排序 + 贪心:将数组排序后,从最小元素开始处理。最小元素肯定不可能是其他元素的双倍,因此必须是原始数组中的元素。
哈希表计数:使用哈希表记录每个元素的出现次数,对于每个原始元素,检查其双倍是否存在且数量足够。
算法步骤
- 首先检查数组长度是否为偶数,奇数长度直接返回空数组
- 使用哈希表统计每个元素的频次
- 将所有不同的元素按升序排序
- 对每个元素 x:
- 如果 x 的频次为 0,跳过(已被消耗)
- 检查 2x 是否存在且频次足够
- 将 x 加入结果,同时减少 x 和 2x 的频次
- 检查是否所有元素都被正确配对
特别注意:0 是特殊情况,因为 0 的双倍还是 0,所以 0 的个数必须是偶数。
代码实现
class Solution {
public:
vector<int> findOriginalArray(vector<int>& changed) {
int n = changed.size();
if (n % 2 == 1) return {};
unordered_map<int, int> count;
for (int num : changed) {
count[num]++;
}
vector<int> keys;
for (auto& p : count) {
keys.push_back(p.first);
}
sort(keys.begin(), keys.end());
vector<int> result;
for (int key : keys) {
if (count[key] == 0) continue;
int doubled = key * 2;
if (count[doubled] < count[key]) return {};
for (int i = 0; i < count[key]; i++) {
result.push_back(key);
}
count[doubled] -= count[key];
count[key] = 0;
}
return result;
}
};
class Solution:
def findOriginalArray(self, changed: List[int]) -> List[int]:
n = len(changed)
if n % 2 == 1:
return []
count = Counter(changed)
result = []
for key in sorted(count.keys()):
if count[key] == 0:
continue
doubled = key * 2
if count[doubled] < count[key]:
return []
result.extend([key] * count[key])
count[doubled] -= count[key]
count[key] = 0
return result
public class Solution {
public int[] FindOriginalArray(int[] changed) {
int n = changed.Length;
if (n % 2 == 1) return new int[0];
var count = new Dictionary<int, int>();
foreach (int num in changed) {
count[num] = count.GetValueOrDefault(num, 0) + 1;
}
var keys = count.Keys.ToList();
keys.Sort();
var result = new List<int>();
foreach (int key in keys) {
if (count[key] == 0) continue;
int doubled = key * 2;
if (!count.ContainsKey(doubled) || count[doubled] < count[key]) {
return new int[0];
}
for (int i = 0; i < count[key]; i++) {
result.Add(key);
}
count[doubled] -= count[key];
count[key] = 0;
}
return result.ToArray();
}
}
var findOriginalArray = function(changed) {
if (changed.length % 2 !== 0) return [];
const count = new Map();
for (const num of changed) {
count.set(num, (count.get(num) || 0) + 1);
}
const result = [];
const sorted = [...changed].sort((a, b) => a - b);
for (const num of sorted) {
if (count.get(num) > 0) {
const doubled = num * 2;
if (count.get(doubled) > 0) {
result.push(num);
count.set(num, count.get(num) - 1);
count.set(doubled, count.get(doubled) - 1);
} else {
return [];
}
}
}
return result;
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log n) | 主要消耗在排序操作 |
| 空间复杂度 | O(n) | 哈希表存储元素频次 |
相关题目
. Array of Doubled Pairs (Medium)
. Recover the Original Array (Hard)