Skip to content

Commit a224069

Browse files
author
robot
committed
feat: 2007
1 parent 138788e commit a224069

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ leetcode 题解,记录自己的 leetcode 解题之路。
413413
- [1899. 合并若干三元组以形成目标三元组](./problems/1899.merge-triplets-to-form-target-triplet.md) 👍
414414
- [1904. 你完成的完整对局数](./problems/1904.the-number-of-full-rounds-you-have-played.md)
415415
- [1906. 查询差绝对值的最小值](./problems/1906.minimum-absolute-difference-queries.md)
416+
- [2007. 从双倍数组中还原原数组](./problems/2007.find-original-array-from-doubled-array.md)
416417
- [2008. 出租车的最大盈利](./problems/2008.maximum-earnings-from-taxi.md)
417418
- [5935. 适合打劫银行的日子](./problems/5935.find-good-days-to-rob-the-bank.md)
418419
- [5936. 引爆最多的炸弹](./problems/5936.detonate-the-maximum-bombs.md)

SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@
262262
- [1899. 合并若干三元组以形成目标三元组](./problems/1899.merge-triplets-to-form-target-triplet.md) 👍
263263
- [1904. 你完成的完整对局数](./problems/1904.the-number-of-full-rounds-you-have-played.md)
264264
- [1906. 查询差绝对值的最小值](./problems/1906.minimum-absolute-difference-queries.md)
265+
- [1906. 查询差绝对值的最小值](./problems/1906.minimum-absolute-difference-queries.md)
266+
- [2007. 从双倍数组中还原原数组](./problems/2007.find-original-array-from-doubled-array.md)
265267
- [2008. 出租车的最大盈利](./problems/2008.maximum-earnings-from-taxi.md)
266268
- [5935. 适合打劫银行的日子](./problems/5935.find-good-days-to-rob-the-bank.md)
267269
- [5936. 引爆最多的炸弹](./problems/5936.detonate-the-maximum-bombs.md)
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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+
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)

0 commit comments

Comments
 (0)