|
| 1 | +## 题目地址(2007. 从双倍数组中还原原数组) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/find-original-array-from-doubled-array/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +``` |
| 8 | +一个整数数组 original 可以转变成一个 双倍 数组 changed ,转变方式为将 original 中每个元素 值乘以 2 加入数组中,然后将所有元素 随机打乱 。 |
| 9 | +
|
| 10 | +给你一个数组 changed ,如果 change 是 双倍 数组,那么请你返回 original数组,否则请返回空数组。original 的元素可以以 任意 顺序返回。 |
| 11 | +
|
| 12 | + |
| 13 | +
|
| 14 | +示例 1: |
| 15 | +
|
| 16 | +输入:changed = [1,3,4,2,6,8] |
| 17 | +输出:[1,3,4] |
| 18 | +解释:一个可能的 original 数组为 [1,3,4] : |
| 19 | +- 将 1 乘以 2 ,得到 1 * 2 = 2 。 |
| 20 | +- 将 3 乘以 2 ,得到 3 * 2 = 6 。 |
| 21 | +- 将 4 乘以 2 ,得到 4 * 2 = 8 。 |
| 22 | +其他可能的原数组方案为 [4,3,1] 或者 [3,1,4] 。 |
| 23 | +
|
| 24 | +
|
| 25 | +示例 2: |
| 26 | +
|
| 27 | +输入:changed = [6,3,0,1] |
| 28 | +输出:[] |
| 29 | +解释:changed 不是一个双倍数组。 |
| 30 | +
|
| 31 | +
|
| 32 | +示例 3: |
| 33 | +
|
| 34 | +输入:changed = [1] |
| 35 | +输出:[] |
| 36 | +解释:changed 不是一个双倍数组。 |
| 37 | +
|
| 38 | +
|
| 39 | + |
| 40 | +
|
| 41 | +提示: |
| 42 | +
|
| 43 | +1 <= changed.length <= 105 |
| 44 | +0 <= changed[i] <= 105 |
| 45 | +``` |
| 46 | + |
| 47 | +## 前置知识 |
| 48 | + |
| 49 | +- 哈希表 |
| 50 | + |
| 51 | +## 公司 |
| 52 | + |
| 53 | +- 暂无 |
| 54 | + |
| 55 | +## 思路 |
| 56 | + |
| 57 | +由于 0 乘以 2 等于自身,因此这种情况比较特殊,我们先考虑其他一般情况,最后再加 0 这个特判。 |
| 58 | + |
| 59 | +由于 changed 中的最小值一定是原数组中的最小值,同理 changed 中的最大值是原数组中的最大值乘以 2.因此实际上,我们可以**确定性得出原数组的两个数**了。 |
| 60 | + |
| 61 | +那么剩下的数呢?我们可以利用**贪心消除法**来解决。 |
| 62 | + |
| 63 | +即先对 changed 进行排序,并从小到大进行处理。对于 1 <= i <= n - 2, changed[i] 其可能是原数组中的值,也可能是原数组 double 后的值。但是如果其 `2 * changes[i]` 存在于 changed 中,那么其一定是原数组中的值。 |
| 64 | + |
| 65 | +> 这个结论成立的前提是后面讲的 "遇到这样的匹配我们就将匹配的双方消除"。 试想如果基于这种消除的思想这个结论不成立,那么 changes[i] 一定不会被消除,而只要有一个无法被消除,就是无解的。 |
| 66 | +
|
| 67 | +这样我们就找到了一对 (changed[i], `2 * changed[i]`),将 changes[i] 加入 ans,并将 `2 * changed[i]` 从 changed 中移除。 |
| 68 | + |
| 69 | +算法: |
| 70 | + |
| 71 | +- 对 changed 进行排序,这样从左到右遍历的时候,我们可以确保枚举到的是原数组中的项(成立的前提依旧是上面提到的消除) |
| 72 | +- 遍历 changed。 如果 changed[i] * 2 存在且可以和 changed[i] 消除(个数足够,换句话说就是 changed[i] 数目不大于 changed[i]*2 的数目),则进行消除。 |
| 73 | + |
| 74 | +如果最后 ans 长度是 changed 一半,说明我们找到了答案,返回即可。否则返回空数组。 |
| 75 | + |
| 76 | +## 关键点 |
| 77 | + |
| 78 | +- 对 changed 进行排序后再处理 |
| 79 | + |
| 80 | +## 代码 |
| 81 | + |
| 82 | +- 语言支持:Python3 |
| 83 | + |
| 84 | +Python3 Code: |
| 85 | + |
| 86 | +```python |
| 87 | + |
| 88 | +class Solution: |
| 89 | + def findOriginalArray(self, changed: List[int]) -> List[int]: |
| 90 | + counter = collections.Counter(changed) |
| 91 | + if counter[0] % 2: return [] |
| 92 | + n = len(changed) |
| 93 | + changed.sort() |
| 94 | + ans = [] |
| 95 | + for c in changed: |
| 96 | + if counter[c] < 1: continue |
| 97 | + double = c * 2 |
| 98 | + if double in counter: |
| 99 | + ans.append(c) |
| 100 | + else: |
| 101 | + return [] |
| 102 | + if double == 0: |
| 103 | + counter[double] -= 2 |
| 104 | + else: |
| 105 | + counter[double] -= 1 |
| 106 | + counter[c] -= 1 |
| 107 | + if len(ans) == n // 2: return ans |
| 108 | + return [] |
| 109 | + |
| 110 | +``` |
| 111 | + |
| 112 | +**复杂度分析** |
| 113 | + |
| 114 | +令 n 为数组长度。 |
| 115 | + |
| 116 | +- 时间复杂度:$O(n)$ |
| 117 | +- 空间复杂度:$O(n)$ |
| 118 | + |
| 119 | +## 相关题目 |
| 120 | + |
| 121 | +- [5966. 还原原数组](https://leetcode-cn.com/problems/recover-the-original-array/) 2007 和这道题思路类似,都是消除思想。这道题的难点在于 k 是未知的,我们需要先枚举出 k,然后再利用消除思想解决。参考代码: |
| 122 | + |
| 123 | +```py |
| 124 | +class Solution: |
| 125 | + def recoverArray(self, nums: List[int]) -> List[int]: |
| 126 | + n = len(nums) |
| 127 | + nums.sort() |
| 128 | + for i in range(n): |
| 129 | + # enumerate i, assueme that: nums[i] is higher[0] |
| 130 | + d = nums[i] - nums[0] |
| 131 | + if d == 0 or d & 1: continue # k 应该是大于 0 的整数 |
| 132 | + k = d // 2 |
| 133 | + counter = collections.Counter(nums) |
| 134 | + ans = [] |
| 135 | + for key in sorted(counter): |
| 136 | + if counter[key + 2 * k] >= counter[key]: |
| 137 | + ans += [key + k] * counter[key] |
| 138 | + counter[key + 2 * k] -= counter[key] |
| 139 | + else: |
| 140 | + break # 剪枝(不剪枝的话实测 Python 也能通过,不过要多花很多时间) |
| 141 | + if len(ans) == n // 2: return ans |
| 142 | + return [] |
| 143 | +``` |
| 144 | + |
| 145 | +> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。 |
| 146 | +
|
| 147 | +力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~ |
| 148 | + |
| 149 | +以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 |
| 150 | + |
| 151 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 152 | + |
| 153 | + |
0 commit comments