2007. 从双倍数组中还原原数组

【算法题解析】还原双倍数组 — 从打乱的数组恢复原数组

题目描述

给定一个整数数组 changed,该数组是通过对一个原始数组 original 的每个元素乘以 2 并打乱顺序后得到的。你的任务是判断给定的 changed 是否为某个 original 数组的双倍数组,并返回该原数组。

具体来说,存在一个数组 original,使得对 original 中的每个元素 xchanged 中都包含 x2 * x 两个元素(顺序可能被打乱)。如果 changed 是这样的双倍数组,返回原始数组 original(元素顺序不限),否则返回空数组。


示例

示例 1:

输入:changed = [1,3,4,2,6,8]
输出:[1,3,4]
解释:
一个可能的 original 数组为 [1,3,4]:
- 1 * 2 = 2,2 出现在 changed 中
- 3 * 2 = 6,6 出现在 changed 中
- 4 * 2 = 8,8 出现在 changed 中

示例 2:

输入:changed = [6,3,0,1]
输出:[]
解释:
不存在一个 original,使得 changed 是它的双倍数组。

示例 3:

输入:changed = [1]
输出:[]
解释:
数组长度为1,不能构成双倍数组。

解题分析

这道题目本质是对数组元素的配对问题,给定一个“打乱”的数组 changed,试图找到一组元素 original,满足 changed 是将 originaloriginal 中每个元素的双倍元素合并打乱后的结果。

关键点和难点在于:

  • 需要确保每个元素 xchanged 中有对应的 2*x 来匹配。
  • 数组可能包含重复元素。
  • 特殊元素 0 需要特殊处理,因为 0 的双倍还是 0,必须成对出现。
  • 原始数组长度是 changed 长度的一半,因此 changed 长度必须为偶数。
  • 配对顺序很重要,应优先配对较小元素,避免先匹配了较大的导致匹配失败。

解题方法

步骤1:初步筛选

  • 如果 changed 长度是奇数,直接返回空数组,因为无法成对。

步骤2:统计频率

  • 使用哈希表(Python 中的 collections.Counter)统计 changed 中每个元素出现的次数。

步骤3:按升序遍历所有元素,尝试配对

  • 按升序遍历频率表中的元素 x
  • 如果频率为0,跳过(已被匹配)。
  • 如果 x == 0,则必须保证频率为偶数,且配对元素也是 0 本身。
  • 对于其他元素,检查其双倍元素 2*x 的频率是否足够与当前元素频率匹配。
  • 如果不够,说明无法配对,返回空数组。
  • 匹配成功后,将原数组中添加相应数量的 x,更新频率,减少 2*x 的计数。

步骤4:返回构造的原始数组


代码实现(Python)

from collections import Counter
from typing import List

class Solution:
    def findOriginalArray(self, changed: List[int]) -> List[int]:
        if len(changed) % 2 != 0:
            return []
        
        count = Counter(changed)
        original = []
        
        for x in sorted(count.keys()):
            if count[x] == 0:
                continue
            
            # 特殊情况:x = 0,必须偶数个才能匹配
            if x == 0:
                if count[x] % 2 != 0:
                    return []
                original.extend([0] * (count[x] // 2))
                count[x] = 0
                continue
            
            double_x = 2 * x
            if count[double_x] < count[x]:
                return []
            
            original.extend([x] * count[x])
            count[double_x] -= count[x]
            count[x] = 0
        
        return original

复杂度分析

  • 时间复杂度:排序键值需要 O(k log k),k 是不同数字个数。整体遍历频率表为 O(n),n 是数组长度。总时间复杂度为 O(n log n)。
  • 空间复杂度:使用了哈希表计数,最坏情况下空间复杂度为 O(n)。

示例说明

以示例1为例:

changed = [1,3,4,2,6,8]

  1. 统计频率:
数字 频率
1 1
2 1
3 1
4 1
6 1
8 1
  1. 排序后遍历:
  • x = 1,检查 2 * 1 = 2,2 的频率是1,满足匹配。
    • 将 1 加入 original,减少 2 的频率为0。
  • x = 2,频率为0,跳过。
  • x = 3,检查 6,频率满足匹配。
    • 将 3 加入 original,减少 6 的频率为0。
  • x = 4,检查 8,频率满足匹配。
    • 将 4 加入 original,减少 8 的频率为0。
  • x = 6 和 8 频率均为0,跳过。
  1. 最终 original = [1,3,4]

其他补充

  • 对于 0 的特殊处理,是本题的关键细节,因为 0 的双倍仍是 0,只能成对出现,否则不符合双倍数组规则。
  • 题目中没有要求原数组的顺序,所以返回的数组顺序不影响正确性。
  • 如果想获得所有可能的原数组,需要更复杂的回溯算法,但本题只需返回一种即可。

总结

这道题通过计数和排序来匹配元素及其双倍,是典型的哈希表应用。合理处理特殊元素和边界条件,是确保算法正确的关键。掌握这类问题对理解数组配对、频率统计和排序有重要帮助。

你可能感兴趣的:(2007. 从双倍数组中还原原数组)