赞
踩
力扣题目链接:https://leetcode.cn/problems/find-original-array-from-doubled-array/
一个整数数组 original
可以转变成一个 双倍 数组 changed
,转变方式为将 original
中每个元素 值乘以 2 加入数组中,然后将所有元素 随机打乱 。
给你一个数组 changed
,如果 change
是 双倍 数组,那么请你返回 original
数组,否则请返回空数组。original
的元素可以以 任意 顺序返回。
示例 1:
输入:changed = [1,3,4,2,6,8] 输出:[1,3,4] 解释:一个可能的 original 数组为 [1,3,4] : - 将 1 乘以 2 ,得到 1 * 2 = 2 。 - 将 3 乘以 2 ,得到 3 * 2 = 6 。 - 将 4 乘以 2 ,得到 4 * 2 = 8 。 其他可能的原数组方案为 [4,3,1] 或者 [3,1,4] 。
示例 2:
输入:changed = [6,3,0,1] 输出:[] 解释:changed 不是一个双倍数组。
示例 3:
输入:changed = [1] 输出:[] 解释:changed 不是一个双倍数组。
提示:
1 <= changed.length <= 105
0 <= changed[i] <= 105
使用哈希表记录每个元素出现(剩余)的次数。对原始数组排个序,接着遍历原始数组:
为什么要排序?因为排序后遍历结果保证是从小到大,一个元素要么已经被“消耗”(则其为二倍元素),要么(其为一倍元素)必须消耗一个二倍元素。
class Solution { public: vector<int> findOriginalArray(vector<int>& changed) { sort(changed.begin(), changed.end()); vector<int> a(200000 + 1); // 手动unordered_map // 2倍防越界 for (int t : changed) { a[t]++; } vector<int> ans; for (int t : changed) { if (!a[t]) { continue; } a[t]--; if (!a[t * 2]) { return {}; } a[t * 2]--; ans.push_back(t); } return ans; } };
为什么使用数组模拟哈希表?因为内置的unordered_map
没试,unordered_multiset
非常慢会超时。
方法一时间复杂度的瓶颈是排序,排序是为了在遍历过程中判断一个元素是“一倍元素”还是“二倍元素”。
有没有办法不排序呢?当然有,那就是遍历过程中,“遇到1/2元素则先跳过”:
遍历到元素 t t t,如果 t 2 \frac{t}{2} 2t还存在,就先跳过 t t t,遍历到 t 2 \frac{t}{2} 2t时再处理 t t t。
这样就需要一个计数器记录还剩下多少元素(防止两个 t t t和一个 t 2 \frac{t}{2} 2t的情况),以及特判 0 0 0的情况(因为 0 2 = 0 \frac{0}{2}=0 20=0不能跳过)。
完了吗?没完,还需要加个while循环
:
给定一组样例
[2,4,2,1]
,如果按照上述思路则会:跳过 2 2 2、跳过 4 4 4、跳过 2 2 2,处理 1 1 1,最终剩下一个 2 2 2和 4 4 4,本应是一对确误判有所剩余。这是因为对于 4 4 4本来应该在遍历到 2 2 2时处理,结果遍历到 2 2 2时一看有 1 1 1跳过了。
因此遇到 t 2 \frac{t}{2} 2t不存在的 t t t时,应当在 t t t有剩余时,不断“反推”,令 t = 2 t t=2t t=2t并继续“抵消”,直到 t t t无剩余。
这样对于
[2,4,2,1]
:处理到 1 1 1时 t = 1 t=1 t=1, t 2 = 0.5 \frac{t}{2}=0.5 2t=0.5不存在,因此抵消 t t t和 2 t 2t 2t(配对成功一个 1 1 1和 2 2 2)
令 t = 2 t = 2 t=2t=2 t=2t=2,抵消 2 2 2和 4 4 4(配对成功一个 2 2 2和 4 4 4)
令 t = 2 t = 4 t = 2t = 4 t=2t=4,发现 4 4 4已经不存在了,结束
最终得到原始数组
[1, 2]
完了吗?没完,单单 2 t 2t 2t不存在了还不能结束循环,要判断 4 t 4t 4t是否存在:
给定样例
[4,8,2,1]
,处理到 4 4 4、 8 8 8、 2 2 2时都会跳过,而处理到 1 1 1时:t = 1 t=1 t=1发现 1 1 1和 2 2 2
t = 2 t = 2 t=2t=2 t=2t=2发现 2 2 2不存在了,循环结束
则会剩下一个本应是一对的 4 4 4和 8 8 8。
因此,在
while循环
中,不能单单地令 t = 2 t t=2t t=2t做这一步的反推。当 2 t 2t 2t已经不存在时,应该令 t = 4 t t=4t t=4t。
若 4 t 4t 4t也不存在,再结束
while循环
。
至此,算法达成。
降低了时间复杂度,代价是代码变得很长且很容易有没考虑周全的地方。
class Solution { public: vector<int> findOriginalArray(vector<int>& changed) { vector<int> a(400001); for (int t : changed) { a[t]++; } int remain = changed.size(); vector<int> ans; if (a[0] % 2) { return {}; } remain -= a[0]; ans.resize(a[0] / 2); a[0] = 0; for (int t : changed) { if (t % 2 == 0 && a[t / 2]) { continue; } while (a[t]) { int thisCnt = a[t]; a[t] = 0; remain -= thisCnt; if (a[t * 2] < thisCnt) { return {}; } a[t * 2] -= thisCnt; remain -= thisCnt; ans.insert(ans.end(), thisCnt, t); if (a[t * 2]) { t *= 2; } else { t *= 4; } } } return remain ? vector<int>() : ans; } };
同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/137924488
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。