LeetCode 2007.从双倍数组中还原原数组:哈希表——从nlogn到n

【LetMeFly】2007.从双倍数组中还原原数组:哈希表——从nlogn到n

力扣题目链接: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

方法一:哈希表 + 排序

使用哈希表记录每个元素出现(剩余)的次数。对原始数组排个序,接着遍历原始数组:

  • 如果这个元素已经被“消耗”了,则跳过;
  • 否则,“移除”这个元素。这个元素的二倍必须在哈希表中:
    • 若在,则找到“一对”,记入答案数组中,并将二倍元素移除;
    • 否则,无法还原,返回空数组。

为什么要排序?因为排序后遍历结果保证是从小到大,一个元素要么已经被“消耗”(则其为二倍元素),要么(其为一倍元素)必须消耗一个二倍元素。

  • 时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),其中 n = l e n ( c h a n g e d ) n=len(changed) n=len(changed)时间复杂度的瓶颈在于排序
  • 空间复杂度 O ( n ) O(n) O(n),或者说 O ( C ) O(C) O(C),其中 C = r a n g e ( c h a n g e d ) = 1 0 5 C=range(changed)=10^5 C=range(changed)=105

AC代码

C++
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循环

至此,算法达成。

  • 时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度 O ( n ) O(n) O(n),或者说 O ( C ) O(C) O(C),其中 C = r a n g e ( c h a n g e d ) = 1 0 5 C=range(changed)=10^5 C=range(changed)=105

AC代码

C++

降低了时间复杂度,代价是代码变得很长且很容易有没考虑周全的地方。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/40.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

环形链表的约瑟夫问题(牛客网)

/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param n int整型 * param m int整型 * return int整型*/struct ListNode * BuyNode(int n)//创建节点和成环{ struct ListNode *pheadNULL;struct ListNode *ptailN…

吐血整理!跨境电商全年选品方向!一年12个月热点解析

一月 演出服、礼盒、贺卡、装饰品、彩带、拉花、红地毯、邀请函、荧光棒、泡泡机等。 二月 超级碗&#xff1a;投影仪、蓝牙音箱、超级碗电子游戏、望远镜、运动类产品等&#xff1b; 情人节&#xff1a;珠宝、服饰饰品、巧克力、香水、口红、烘焙用品、礼盒、个人护理、成…

MySQL常用命令和函数的讲解以及表之间的联结

Mysql的中一些语句的用法&#xff1a; 有表&#xff1a; CREATE TABLE book (id int(20) NOT NULL,book_name varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 书名,press varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NUL…

pymavlink 解析自定义mavlink消息。

1.下载mavlink_master包&#xff0c;用于将xml 文件生成对应的py文件。地址是 https://codeload.github.com/mavlink/mavlink/zip/refs/heads/master 进入目录运行python .\mavgenerate.py 呈现gui程序 2.根据发送端的消息定义格式修改接收解析段的pymavlink有关库。 可修改……

基于大数据的手机销售数据分析可视化系统,爬取京东和淘宝的的手机商品数据进行分析,Flask,Python,数据可视化

介绍 该系统主要是通过爬取京东和淘宝的的手机商品数据进行分析。爬虫python脚本通过打开浏览器授权登录后按照搜索“手机”关键字后出现的商品列表进行爬取&#xff0c;获取标题名&#xff0c;解析付款人数&#xff0c;品牌&#xff0c;评论人数&#xff0c;发货地&#xff0…

自用-常用词

PHP 常用 file_put_contents("awlog.txt", ---time:.date(Y-m-d H:i:s,time()).---xml:.$GLOBALS[HTTP_RAW_POST_DATA].var_export($_POST,TRUE).PHP_EOL, FILE_APPEND); error_reporting(0); register_shutdown_function(function(){ var_dump(error_get_last()…

算法学习——LeetCode力扣补充篇14(179. 最大数、43. 字符串相乘、32. 最长有效括号、543. 二叉树的直径、113. 路径总和 II)

算法学习——LeetCode力扣补充篇14 179. 最大数 179. 最大数 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一组非负整数 nums&#xff0c;重新排列每个数的顺序&#xff08;每个数不可拆分&#xff09;使之组成一个最大的整数。 注意&#xff1a;输出结果可能非常大&…

吴恩达2022机器学习专项课程(一) 第二周课程实验:特征缩放和学习率(多元)(Lab_03)

备注&#xff1a;笔者只对个人认为的重点代码做笔记&#xff0c;其它详细内容请参考吴恩达老师实验里的笔记。 1.多元特征的训练集 调用load_house_data()函数&#xff0c;将训练集数据保存到数组中。 X&#xff0c;y分别存储所有训练样本的前四列&#xff0c;所有训练样本的…

C# argb格式的raw文件转换为RGB文件

在C#中&#xff0c;如果你想将一个ARGB格式的raw文件转换为RGB格式的raw文件&#xff0c;你需要遍历原始数据&#xff0c;忽略Alpha通道&#xff08;透明度&#xff09;&#xff0c;并仅保留RGB值。 ARGB格式中每个像素占用32位&#xff08;8位Alpha&#xff0c;8位Red&#x…

python中的列表、元组、字典、集合(集合篇)

数据类型定义符号访问元素是否可变是否重复是否有序列表 [ ]索引可变可重复有序元组&#xff08;&#xff09;索引不可变可重复有序字典{key&#xff1a;value}键可变可重复无序集合{ }可变不可重复无序 基本概念 python语言中的集合是无序的、可变的容器类对象&#xff0c;所…

4.16作业

1.总结keil5下载代码和编译代码需要注意的事项 一、在编译代码时需要先点击魔术棒点击 修改flash Downlond 和pack 二、可以通过F12转跳到对应的函数中&#xff0c;查看函数的原型 三、注释出现乱码通过 Edit中的中的来修改 四、要先bulid在load 2.总结STM32Cubemx的使用方…

NLP学习(1)-搭建环境

前言 仅记录学习笔记&#xff0c;如有错误欢迎指正。 环境搭建 一、环境软件安装&#xff1a; 1、Anaconda安装&#xff08;一款可以同时创建和管理多个python环境的软件&#xff09; (1) 安装链接&#xff1a; https://blog.csdn.net/m0_61531676/article/details/126290…

最短路径算法(Dijkstra算法、Floyd-Warshall算法)

最短路径算法是解决图论中节点之间最短路径问题的经典算法。以下是两种常见的最短路径算法&#xff1a;Dijkstra算法和Floyd-Warshall算法。 Dijkstra算法 Dijkstra算法用于解决单源最短路径问题&#xff0c;即给定一个起点&#xff0c;找到起点到其他所有节点的最短路径。 …

【python】描述性统计计算偏斜度和峭度

文章目录 1.编写计算偏斜度和峭度的函数。并用自己编写的函数计算课本23页的习题1.5数据的偏斜度和峭度。2.从1.5数据中随机抽取2个容量为20的样本&#xff0c;分别计算它们的平均数和标准差3.请绘制给定数据的频率分布直方图&#xff0c;计算数据的均值、标准差、偏斜度和峭度…

【经典算法】 leetcode88.合并排序的数组(Java/C/Python3实现含注释说明,Easy)

作者主页&#xff1a; &#x1f517;进朱者赤的博客 精选专栏&#xff1a;&#x1f517;经典算法 作者简介&#xff1a;阿里非典型程序员一枚 &#xff0c;记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法&#xff08;公众号同名&#xff09; ❤️觉得文章还…

学习循环神经网络(RNN)

1. 掌握基础知识 理解RNN的基本概念&#xff1a;RNN是一种神经网络&#xff0c;它包含循环&#xff0c;使得网络能够维持一个内部状态&#xff0c;从而对序列的元素进行处理。学习RNN的工作机制&#xff1a;了解RNN是如何通过时间步迭代处理信息的&#xff0c;以及如何利用前一…

Ribbon 添加右侧区域菜单项

效果图如下所示&#xff1a; 类似与上图效果所示&#xff0c;代码如下&#xff1a; RibbonPage* pageHome1 ribbonBar()->addPage(tr("Home")); //实现代码&#xff1a; { QMenu* menuOptions ribbonBar()->addMenu(tr("Options"))…

古籍数字化平台:精校功能介绍

一、平台介绍 古籍数字化平台&#xff0c;本着公益性、低成本、合作共赢的三大原则&#xff0c;功能涵盖古籍OCR识别、族谱县志OCR识别、民国报纸OCR识别、图文逐字校对、数据著录、智能标点分段、精编排版、智能白话译文等&#xff0c;是一站式线上整理全流程平台。 平台集成…

C语言 【基础语法】

一、编程环境搭建 编译器&#xff1a;gcc 集成开发环境&#xff1a;vscode 1.1 安装vscode 1.2 设置中文包 插件 1.3 设置C/C扩展 安装 C/C Compile Run extension 和 C/C Extension Pack 扩展 二、基础语法 2.1 第一个c语言程序 2.2 数据类型 2.2.1 变量的语法(重点) …

Linux系统安装ansible

安装ansible yum install epel-release -y yum install ansible -y#检查是否安装成功 ansible --version检测ansible是否与其他机器连通 #需要先在/etc/ansible/hosts文件中进行配置 #并且需要配置免密登录#检测自己本机是否正常 ansible localhost -m ping #检测与主机host…