《程序员面试金典(第6版)》面试题 16.21. 交换和(哈希法,双指针,二分查找)

news2024/11/30 12:54:09

题目描述

给定两个整数数组,请交换一对数值(每个数组中取一个数值),使得两个数组所有元素的和相等。题目入口

  • 返回一个数组,第一个元素是第一个数组中要交换的元素,第二个元素是第二个数组中要交换的元素。若有多个答案,返回任意一个均可。若无满足条件的数值,返回空数组。

示例:

输入: array1 = [4, 1, 2, 1, 1, 2], array2 = [3, 6, 3, 3]
输出: [1, 3]

示例:

输入: array1 = [1, 2, 3], array2 = [4, 5, 6]
输出: []

提示:

  • 1 <= array1.length, array2.length <= 100000

解题思路与代码

  • 首先呢,这道题看起来是一道十分清爽的题。为什么呢?因为说的是人话,清醒易懂。题目的难度呢?中等偏简单。

  • 这道题的核心其实是让你找出一种比暴力破解时间复杂度更小的做法出来(暴力破解的时间复杂度为O(n^2))

  • 那么这道题就很有趣了。我自己采取的办法是哈希法,因为哈希法可以使查找的复杂度从O(n),去降低到O(1)。至于为什么,那你可能就要去学习或了解一下哈希的底层原理了。推荐看我的这篇文章,介绍的很详细:全面理解哈希,哈希的底层原理是如何实现的,哈希题型的做题思路与题目清单(不断更新)

  • 还有两种方法,一种是使用排序 + 双指针,一种是排序 + 二分法。这两种的时间复杂度与空间复杂度都还行,与第一种方法相比,这两种的空间复杂度要明显的优于第一种方法,而时间复杂度,明显的要比第一种差很多。

那么接下来,就让我来看看这几种方法,在实现上都有些什么区别吧~

方法一:哈希法

  • 我们首先需要计算出两个数组的和分别是多少。然后需要去判断,两个数组的差是否是奇数,如果是奇数,那是不可能交换成功的,所以需要返回空。

  • 紧接着,我们创建一个unordered_set<int> set,让set里面充满数组1的元素。其次,我们再创建一个变量diff,它记录着两个数组和的差除以2的值。这个数必定是一个整数。所以不用担心会除出小数来。

  • 这个数的目的就是就是为了再遍历数组2的时候,去数组1中去寻找,是否有一个数n1 = diff + n2,找到了,就返回n1,n2。

  • 如果最后都找不到,那只能返回空了。

具体的代码如下:

class Solution {
public:
    vector<int> findSwapValues(vector<int>& array1, vector<int>& array2) {
        int a1 = arraySum(array1);
        int a2 = arraySum(array2);
        if((a1 - a2) % 2 != 0) return {};
        int diff = (a1 - a2) / 2;
        unordered_set<int> set(array1.begin(),array1.end());
        for(auto& n2 : array2){
            int n1 = n2 + diff;
            if(set.count(n1)) return{n1,n2};
        }
        return {};
    }
    int arraySum(vector<int>& array){
        int size = 0;
        for(auto& a : array)
            size += a;
        return size;
    }
};

复杂度分析

时间复杂度

  • 这段代码的时间复杂度是 O(n)。原因是 arraySum 函数遍历了 array1 和 array2 各一次,每次遍历的时间复杂度是 O(n),然后在构建 unordered_set set1 时,又遍历了一次 array1,时间复杂度也是 O(n)。最后在遍历 array2 时,查找操作的平均时间复杂度是 O(1),所以总的遍历操作的时间复杂度是 O(n)。因此,整个代码的时间复杂度是 O(n)。

空间复杂度

  • 这段代码的空间复杂度也是 O(n)。原因是使用了一个 unordered_set 来存储 array1 的所有元素,unordered_set 的大小与 array1 的大小相等,所以空间复杂度是 O(n)。

在这里,n 是 array1 和 array2 的长度之和。如果你想要更精确地表示时间复杂度和空间复杂度,可以使用两个变量,如 n1 和 n2,分别代表 array1 和 array2 的长度。这样,时间复杂度就是 O(n1 + n2),空间复杂度就是 O(n1)。

方法二 排序 + 双指针法

  • 这种方法其实蛮巧妙的,但是需要提前排序。为什么呢?这是因为只有有序的数组,才能使用后面的双指针操作。所以一定要进行排序操作。

  • 和方法一的许多步骤一样,我们需要计算数组的和,需要看数组和差是否为偶数等。这里不一样的一点是,我们用双指针去同时遍历两个数组,用while循环

  • 之前我们创建一个变量 int diff = (a1 - a2) / 2; 这个就是算出两个数组之间差值的一半,我们只需要把多一半的部分与少一半的部分去交换就好了。

  • 在while循环中,双指针指向的都是两个数组的首元素,如果 array1[p1] - array2[p2] < diff 我们就 ++ p1 这是因为只有当 array1[p1] - array2[p2] = diff 正好满足了交换原则,可以交换 ,那小于,说明数组1中的元素小了,该让p1指针动。反之就是p2指针动。刚刚好,就直接返回。

具体的代码如下

class Solution {
public:
    vector<int> findSwapValues(vector<int>& array1, vector<int>& array2) {
        sort(array1.begin(),array1.end());
        sort(array2.begin(),array2.end());
        int a1 = arraySum(array1);
        int a2 = arraySum(array2);
        if((a1 - a2) % 2 != 0) return {};
        int diff = (a1 - a2) / 2;
        int p1 = 0;
        int p2 = 0;
        while(p1 < array1.size() && p2 < array2.size()){
            if(array1[p1] - array2[p2] < diff) ++p1;
            else if(array1[p1] - array2[p2] > diff) ++p2;
            else return {array1[p1],array2[p2]};
        }
        return {};
    }
    int arraySum(vector<int>& array){
        int size = 0;
        for(int& num : array)
            size += num;
        return size;
    }
};

在这里插入图片描述

复杂度分析

时间复杂度:

  • 首先使用了sort()函数进行排序,使用sort()函数的时间复杂度为O(nlogn),while循环遍历的时间复杂度为O(n)
  • 总的时间复杂度为O(nlogn)

空间复杂度:

  • 由于没有使用任何的数据结构,那么空间复杂度为O(1)

方法三: 排序 + 二分法

这种方法与方法二差不多。唯一的差别就在于上面的是使用while循环 + 双指针进行遍历双数组。 这里是使用for循环 + while循环(二分法)去执行遍历数组。

具体是这样的,这样的遍历方式有点像暴力破解了,只不过是内层的for循环,改成了用二分法去查找指定元素罢了。第一层for循环是去把数组1中的每一个元素拿出来,然后 减去一个diff 看能不能在数组2中找到这个元素。

在数组二中就用二分法去查找。二分法其实很简单,具体的看代码实现把,这里实在是不想多费笔墨了。

具体的代码实现如下:

class Solution {
public:
    vector<int> findSwapValues(vector<int>& array1, vector<int>& array2) {
        int a1 = 0;
        int a2 = 0;
        sort(array1.begin(),array1.end());
        sort(array2.begin(),array2.end());
        for(int& num : array1) a1 += num;
        for(int& num : array2) a2 += num;
        if((a1 - a2) % 2 != 0) return {};
        int diff = (a2 - a1) / 2;
        for(int i = 0; i < array1.size(); ++i){
            int left = 0;
            int right = array2.size() - 1;
            int target = array1[i] + diff;
            int mid;
            while(left <= right){
                mid = (left + right) / 2;
                if(target > array2[mid]) left = mid + 1;
                else if(target < array2[mid]) right = mid -1;
                else return {array1[i],array2[mid]};
            }
        }
        return {};
    }
};

在这里插入图片描述

复杂度分析:

时间复杂度:

  • 时间复杂度是 O(n log n)。其中,排序 array2 的时间复杂度为 O(n log n),然后你在 array1 中遍历每个元素,对于每个元素,都进行了一次二分查找,二分查找的时间复杂度为 O(log n),因此,总的时间复杂度为 O(n log n)。

空间复杂度:

  • 空间复杂度是 O(1)。在这个解决方案中,我们没有使用额外的数据结构,所以空间复杂度为 O(1)。

这个方法的效率与双指针方法相比,时间复杂度是相同的,都是 O(n log n)。然而,实际运行速度可能会有所不同,这取决于具体的输入数据和语言实现。在某些情况下,二分查找可能比双指针方法快,尤其是在两个数组大小差距很大的情况下。所以,这两种方法都是值得考虑的。

总结

这道题目是一个典型的算法题,主要考察了解决问题的能力、编程技巧和对算法时间复杂度的把控。通过这道题目,你可以学习和掌握不同的算法方法,如暴力解法、双指针技术、排序与二分查找、哈希法等,并深入理解它们的优缺点。

这道题的意义在于:

  • 培养解决问题的能力:这道题需要找到两个数组中各取一个元素交换后,使得两个数组的元素之和相等。在解决这个问题的过程中,你需要学会如何根据题目要求分析问题,寻找解决方案。

  • 提高编程技巧:通过实现不同的解决方案,你可以提高自己的编程技巧,例如使用双指针技术、排序和二分查找,哈希法等方法。

  • 熟练掌握时间复杂度和空间复杂度:对于不同的解决方案,你需要分析它们的时间复杂度和空间复杂度,并在实际编程中选择最优的方案。

  • 锻炼创新思维:在寻找解决方案的过程中,你可以尝试不同的思路,挑战自己的思维能力,培养创新意识。

通过这道题,你可以锻炼自己在算法和编程领域的能力,对于提高解决问题的能力、编程技巧和深入理解算法具有很好的帮助。

最后的最后,如果你觉得我的这篇文章写的不错的话,请给我一个赞与收藏,关注我,我会继续给大家带来更多更优质的干货内容

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

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

相关文章

vscode编译mysql-boost-5.7.37如何设置Cmake:configuration以及成功debug教程

前提&#xff1a;已经在服务器上编译安装好mysql5.7.37源码了&#xff0c;这里只是记录一下使用vscode远程连接服务器时如何成功debug&#xff0c;这篇与我发布的关于openGauss源码部署有双向链接关系&#xff0c;后续可方便查看。 1. 远程连接服务器进入连接界面 2. 设置Cmak…

《计算机网络—自顶向下方法》 第三章Wireshark实验:DNS协议分析

域名系统 DNS(Domain Name System) 是互联网使用的命名系统&#xff0c;用于把便于大家使用的机器名字转换为 IP 地址。许多应用层软件经常直接使用 DNS&#xff0c;但计算机的用户只是间接而不是直接使用域名系统。 互联网采用层次结构的命名树作为主机的名字&#xff0c;并使…

达索的有限元分析软件Abaqus 2023版本下载与安装配置教程

目录 前言一、安装前准备二、Abaqus 安装总结 前言 Abaqus软件是一款广泛使用的有限元分析软件&#xff0c;可用于模拟各种工程应用程序&#xff0c;包括结构、流体力学、热传递、电磁和声学等。该软件提供了广泛的建模功能和多种求解器&#xff0c;可以帮助工程师预测材料和结…

lowcode-cms开源社区源码设计分享

开源背景 lowcode可视化社区 是我之前在设计研发 Dooring低代码 平台时开发的一个面向低代码内容分享的知识社区, 内容端采用 SSR 技术来渲染页面, 对 SEO 更加友好, 同时后端服务采用 Nodejs 来实现, 内容端和服务端同端, 也就是传说中的内容服务“同构”. 管理端采用前端最最…

【JUC】线程池有哪 4 种拒绝策略?

第一种拒绝策略是 AbortPolicy&#xff0c;这种拒绝策略在拒绝任务时&#xff0c;会直接抛出一个类型为 RejectedExecutionException 的 RuntimeException&#xff0c;让你感知到任务被拒绝了&#xff0c;于是你便可以根据业务逻辑选择重试或者放弃提交等策略。 第二种拒绝策略…

ChatGPT自动生成大厨菜谱

大厨菜谱 最近几年&#xff0c;越来越多年轻人也开始尝试自己在家做菜吃饭&#xff0c;而不仅仅是外出就餐或点外卖。有些人可以向长辈讨教做菜心得&#xff0c;有些人则开始尝试从互联网搜索学习菜谱。著名的"下厨房" App&#xff0c;就是针对这个需求应运而生的。…

华为许超:伙伴成功,才有华为企业业务成功

伙伴&#xff0c;可以说是今年ICT行业最为重要的词。各大厂商都在强调伙伴优先&#xff0c;发力伙伴体系构建。然而行业内更多是厂商单维度的信息释放&#xff0c;重视强调厂商面向伙伴的支持与赋能。这个过程中&#xff0c;似乎普遍缺少一个视角&#xff1a;那就是伙伴究竟需要…

美团一面:Spring Cloud 如何构建动态线程池?

美团一面&#xff1a;Spring Cloud 如何构建动态线程池&#xff1f; 说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如美团、极兔、有赞、希音、百度、网易、滴滴的面试资格&#xff0c;遇到一几个很重要的面试题&#xff1a;…

最强linux抓包工具优劣势对比分析

ngrep 核心参数 参数名称参数介绍-q禁止在屏幕上输出匹配行-d定义网络接口&#xff0c;可以多次使用-W设置缓冲区大小&#xff0c;单位为MB&#xff0c;默认为2MB-n不解析ASCII数据-i设置网络接口&#xff0c;与-d参数相同-P将解析的协议名称作为输出的一部分-t显示时间戳-O输出…

Linux——线程1

背景知识 堆区细粒堆划分 我们在申请堆空间时&#xff0c;我们只是设置了空间大小&#xff0c;并未设置空间从哪里结束。 堆区里面的详细信息&#xff0c;如某段空间从哪开始&#xff0c;从哪结束&#xff0c;由vm_area_struct负责记录&#xff0c;每申请一段空间就增加一个vm_…

优于FCOS:在One-Stage和Anchor-Free目标检测中以最小的成本实现最小的错位(代码待开源)...

关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;ComputerVisionGzq 学习群&#xff5c;扫码在主页获取加入方式 计算机视觉研究院专栏 作者&#xff1a;Edison_G 与基线FCOS&#xff08;一种单阶段和无锚目标象检测模型&#xff09;相比&#xff0c;新提出的模型…

STL-map容器

作为关联式容器的一种&#xff0c;map 容器存储的都是 pair 对象&#xff0c;也就是用 pair 类模板创建的键值对。其中&#xff0c;各个键值对的键和值可以是任意数据类型&#xff0c;包括 C基本数据类型&#xff08;int、double 等&#xff09;、使用结构体或类自定义的类型。…

IDEA中java文件出现黄色的J文件同时maven项目导入了依赖但是idea依赖加不进去的问题记录

IDEA导入项目后依赖jar包没有显示 报错提示尝试的解决方法检查对应的sdkmodule等配置信息开始尝试是不是版本问题&#xff0c;因为对上述maven报错进行查询&#xff0c;好像是因为版本太高导致的&#xff0c;开始下一个低版本进行尝试切换版本即可解决 总结后续出现的BUG 此次环…

Linux基本指令(三)

Linux基本指令 一.data指令1.基本使用2.时间戳 二.cal指令三.find指令四.grep指令五.zip/unzip指令六.tar指令&#xff08;打包/解包&#xff0c;不打开直接看内容&#xff09;七.bc指令八.uname指令九.几个重要热键十.关机 一.data指令 1.基本使用 下面不需要记住&#xff0c…

nodejs+Vue+elementui农家乐管理系统系统-住宿-美食-活动报名系统vue

语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 目 录 管理员在还可以进行首页、个人中心、农家乐管理、美食信息管理、住宿信息管理、活动信息管理、用户管理、活动报名管理、客房预…

English Learning - L3 作业打卡 Lesson1 Day5 2023.5.9 周二

English Learning - L3 作业打卡 Lesson1 Day5 2023.5.9 周二 引言&#x1f349;句1: Fast loud music is popular with many people .成分划分爆破语调 &#x1f349;句2: They may say the music is red hot, especially the kind called Dixieland jazz.成分划分爆破语调 &a…

Ae 入门系列之十二:三维合成

在 Ae 中&#xff0c;可以将图层设置为 3D 图层并添加动态的摄像机和灯光&#xff0c;从而制作基于三维空间的动画。 ◆ ◆ ◆ 二维与三维 二维 2D 在一个平面上的内容就是二维。只有上下、左右两个方向&#xff0c;没有前后。即&#xff0c;只有面积&#xff0c;没有体积。 …

FX DocuCentre S2520~~富士施乐 DocuCentre S2520和2022打印机驱动安装

先确定打印机IP 1. 12楼打印机IP 13楼 2楼 192.168.20.251 192.168.20.252 192.168.10.252 2. 下载安装驱动 2520富士施乐 DocuCentre S2520 驱动下载 - 驱动天空 (drvsky.com) 2022富士施乐 DocuCentre SC2022 驱动下载 - 驱动天空 (drvsky.com) 3. 添加驱动配置 安装驱动配…

解决问题,是管理者最了不起的才华

作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 小C和小Y是K哥下面的两个主管&#xff0c;都管着10多人的团队&#xff0c;但他们带队伍的风格完全不同。小C名校毕业&#xff0c;理论基础扎实&#xff0c;彼得德鲁克、约翰科特的管理思想&#xff0c…

如何解决过度拟合

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…