《程序员面试金典(第6版)》面试题 16.24. 数对和(哈希映射,双指针)

news2024/11/26 2:38:48

题目描述

设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。
题目传送门

示例 1:

输入: nums = [5,6,5], target = 11
输出: [[5,6]]

示例 2:

输入: nums = [5,6,5,6], target = 11
输出: [[5,6],[5,6]]

提示:

  • nums.length <= 100000
  • -10^5 <= nums[i], target <= 10^5

解题思路与代码

  • 这道题属于一道比较简单的题了。我们可以用很多种方法去做它。比如说排序 + 双指针,或者说用哈希映射实现快速查找?但是唯一可能不行的就是暴力破解。那样可能时间复杂度不过关,hh

那么就让我来一一介绍这些可行的方法吧!

方案一:哈希法实现快速查找

  • 对于这种方法,我们首先建立了数组中元素与元素对应的个数的哈希映射,我们用 unordered_map<int,int> map;去实现。
  • 之后呢,我们再去遍历数组找到对应的元素,然后把他们在map中对应的元素个数减去。并存到结果集中
  • 最后返回结果集即可

具体代码如下:

class Solution {
public:
    vector<vector<int>> pairSums(vector<int>& nums, int target) {
        unordered_map<int,int> map;
        for(int& num : nums)
            ++map[num];
        vector<vector<int>> result;
        for(int i = 0; i < nums.size(); ++i){
            int n1 = nums[i];
            if(map[n1]){
                int n2 = target - n1;
                --map[n1];
                if(map[n2]){
                    result.push_back({n1,n2});
                    --map[n2];
                }else{
                    ++map[n1];
                }
            }
        }
        return result;
    }
};

在这里插入图片描述

复杂度分析

时间复杂度:

  • 该算法的时间复杂度主要取决于两个循环。第一个循环用于填充映射,第二个循环用于找到和为目标值的整数对。因为这两个循环都是线性的,所以总的时间复杂度是O(n) + O(n) = O(2n)。但在大O表示法中,我们通常忽略掉常数因子,所以最终的时间复杂度是O(n)。

空间复杂度:

  • 这个算法的空间复杂度主要取决于用于存储输入数组元素和结果的空间。数组的空间占用是O(n),而结果的空间占用最多也是O(n),因为最多只能有n/2个有效的对。因此,总的空间复杂度是O(n) + O(n) = O(2n)。同样,我们在大O表示法中通常忽略掉常数因子,所以最终的空间复杂度是O(n)。

方案二:优化!边遍历数组,边建立哈希映射

  • 就如我们的方案题目说的一样,我们边遍历数组,边建立哈希映射。

  • 在for循环的途中,我们去map中寻找有没有target - nums[i] 的键,如果有,并且对于的值 > 0。我们就可以把它们加入结果集中,并且对键对应的值 - 1。

  • 如果在map中找不到对于的键,我们就把nums[i] 加入map中,并 ++ 它的value。

  • 这样遍历完一次数组,我们也就找到了自己的答案。

  • 相比于方案一,节省了一倍的遍历时间。

具体的代码如下

class Solution {
public:
    vector<vector<int>> pairSums(vector<int>& nums, int target) {
        unordered_map<int,int> map;
        vector<vector<int>> result;
        for(int& num : nums){
            auto it = map.find(target - num);
            if(it != map.end() && it->second > 0){
                result.push_back({it->first,num});
                --it->second;
            }else ++map[num];
        }
        return result;
    }
};

在这里插入图片描述

复杂度分析

时间复杂度:

  • 这段代码的时间复杂度主要由循环决定。对于数组中的每个元素,算法都会在哈希表中进行查找,这是一个常数时间操作(平均情况下)。因此,总的时间复杂度为O(n)。

空间复杂度:

  • 该算法的空间复杂度主要取决于用于存储输入数组元素和结果的空间。哈希表的空间占用是O(n),结果的空间占用最多也是O(n),因为最多只能有n/2个有效的对。因此,总的空间复杂度是O(n) + O(n) = O(2n)。同样,在大O表示法中,我们通常忽略掉常数因子,所以最终的空间复杂度是O(n)。

因此,相比第一段代码,这段代码在时间复杂度上有所优化,因为它将两个操作合并到了一个循环中,但空间复杂度保持不变。

方案三:排序 + 双指针

  • 这种方法也很简单。我们先把数组排序。然后创建两个指针,一个指向数组头元素,一个指向数组尾元素。

  • 之后用while循环来遍历数组,如果前后之和 > target 则right-- ,如果前后之和 < target ,则left ++ , 否则就等于是找到了,把它们添加进结果集中, ++left --right

  • 最后返回结果集即可

具体代码如下:

class Solution {
public:
    vector<vector<int>> pairSums(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());
        int left = 0;
        int right = nums.size() -1;
        while(left < right){
            if(nums[left] + nums[right] > target) --right;
            else if(nums[left] + nums[right] < target) ++left;
            else{
                result.push_back({nums[left],nums[right]});
                ++left;
                --right;
            }
        }
        return result;
    }
};

在这里插入图片描述

复杂度分析

时间复杂度:

  • 这段代码的时间复杂度由排序操作和双指针扫描决定。在最坏的情况下,使用快速排序或归并排序对数组进行排序的时间复杂度为O(n log n)。双指针扫描数组的时间复杂度为O(n)。因此,总的时间复杂度是O(n log n) + O(n)。在大O表示法中,我们取最高阶项,所以最终的时间复杂度是O(n log n)。

空间复杂度:

  • 这段代码的空间复杂度主要取决于用于存储输入数组和结果的空间。数组的空间占用是O(n),结果的空间占用最多也是O(n),因为最多只能有n/2个有效的对。排序操作通常需要O(log n)的额外空间(例如在归并排序中)。因此,总的空间复杂度是O(n) + O(n) + O(log n) = O(2n + log n)。在大O表示法中,我们取最高阶项,所以最终的空间复杂度是O(n)。

这段代码的主要优点是它没有使用额外的哈希表来存储元素的频率,因此可能在空间利用率上稍微优于前两段代码。然而,它的时间复杂度比前两段代码高,因为它需要对数组进行排序。

总结

这道题目是一道经典的算法设计题,主要考察了以下几个方面:

  • 对哈希表数据结构的理解和应用:哈希表是一种常用的数据结构,它可以用于快速查找元素,是许多算法问题的关键。在这道题目中,哈希表被用来记录数组中的元素及其出现次数。

  • 对双指针技术的理解和应用:双指针是一种常用的遍历技术,可以在遍历数组或链表时提供额外的信息。在这道题目中,双指针被用来在有序数组中寻找满足特定条件的元素对。

  • 对排序算法的理解和应用:如果采用双指针的解法,你需要首先对数组进行排序。排序是编程中非常常见的任务,对其理解和掌握是非常重要的。

  • 思维的灵活性:这道题目可以有多种解法,考察了你对问题的理解和分析能力,以及在给定约束条件下寻找最优解的能力。

  • 代码的编写和调试能力:不仅要设计出算法,还要将算法转化为正确且高效的代码,这也是一项非常重要的能力。

在实际的软件开发中,类似的问题也经常会遇到。例如,在一些推荐系统中,可能需要寻找满足某些条件的元素对,这时候就可以借鉴这道题目的解法。

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

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

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

相关文章

抖音SEO矩阵系统源码开发(一)

抖音seo矩阵营销系统/抖音SEO矩阵号管理系统/抖音霸屏源码开发搭建&#xff0c;抖音官方团队大力推广抖音SEO生态&#xff0c;我们应如何布局开发抖音SEO矩阵系统&#xff0c;来达到账号排名优化的效果&#xff0c;很显然&#xff0c;账号关键词起到了很关键的作用。首先&#…

强化学习笔记-06 Temporal-Difference TD时分学习

本文是博主对《Reinforcement Learning- An introduction》的阅读笔记&#xff0c;不涉及内容的翻译&#xff0c;主要为个人的理解和思考。 TD学习是现代强化学习方法的核心&#xff0c;其是蒙特卡罗法和动态规划法的结合&#xff0c;一方面&#xff0c;其同蒙特卡罗法一样&…

opengauss的pull-request

一个pull-request其实是一个分支&#xff0c;而不是一个提交&#xff0c;所以一个pull-request里可以容纳多个提交&#xff0c;pull-request的提交过程原理很像&#xff08;应该就是&#xff09;rebase一个分支&#xff0c;然后将这个分支合入目标分支。 下面是我修改代码并且…

【软件测试】软件测试岗位会越来越少吗?

我的整体意见是测试岗位不会变少&#xff0c;反而相对于其他岗位会变的更重要一些。 首先纠正一个非常非常错误的观念。测试和测试开发是两个岗位&#xff1f;No&#xff0c;不是的。测试开发是属于测试的。 测试开发只不过是使用类似于开发的技术和能力&#xff0c;来达到测…

第十四章 使用Postfix与Dovecot部署邮件系统

文章目录 第十四章 使用Postfix与Dovecot部署邮件系统一、电子邮件系统1、常见的邮件协议2、注意事项 二、部署电子邮件系统1、部署基础的电子邮件系统&#xff08;1&#xff09;、配置服务器主机名称&#xff08;2&#xff09;、配置服务器的DNS地址&#xff08;3&#xff09;…

GDPU C语言 天码行空12 (未完待续)

&#x1f351; C语言实验专栏 编程题 1. 统计文本文件中各类字符的个数 &#x1f351; C语言读写文件 #include <stdio.h> #include<ctype.h> //判断字符类型函数的头文件int main() {FILE *fp1 NULL,*fp2 NULL;fp1 fopen("f1.txt", "w")…

MyBatis练习

阅读教程实例&#xff0c;我们了解原理后可以自己模仿地写一个程序练练手。 1、创建表&#xff0c;然后手动导入数据 create table four kings (id int primary key,name varchar(4) ,idea varchar(20) )CHARSETutf8;; 2、 写一个实体类&#xff0c;对应数据库的表 package …

什么是网络自动化?有多重要?

什么是网络自动化 网络自动化是在网络环境中实现网络和安全自动化的方法&#xff0c;通过减少人力工作量和人为错误来最大限度地提高效率。 网络自动化是自动化每个网络任务&#xff08;如配置管理、备份和监控&#xff09;的过程&#xff0c;以实现管理大型网络基础架构的最…

小红薯评/论数据采集工具

一、小红薯【评/论】采集工具链接&#xff1a; http://106.53.68.168:9920/xhs-comment-spider 二、使用方式 1、打开网页链接后&#xff0c;按照网页各步骤提示进行操作即可。 2、采集数据时&#xff0c;页面右上角显示【RUNNING】代表在运行中&#xff0c;采集结束后&#xf…

【软件测试】初级测试,如何走向中高级软件测试?

关于软件测试这几年是越来越红火&#xff0c;待遇对于其他行业也是非常的高&#xff0c;万八千的待遇很正常&#xff0c;而现在软件测试行业等级越来越专业化&#xff0c;对软件测试工程师的要求也是越来越高&#xff0c;软件测试工程师一般会分为初级软件测试工程师&#xff0…

Web身份验证(WebAuthn)

目录 1、WebAuthn简介 2、FIDO2&#xff1a;客户端到验证器协议&#xff08;CTAP&#xff09; 3、浏览器和平台 4、Web身份验证API 基本流程 5、使用 WebAuthn API 5.1 注册WebAuthn凭据 5.2使用WebAuthn凭据进行身份验证 6、 WebAuthn演示&#xff08;注册和认证&…

IO 与进程线程

IO进程 scanf\printf:终端 IO&#xff1a;input/output&#xff0c;文件 标准IO 文件IO 文件属性获取&#xff1a;ls -l 文件类型 文件权限 链接数 用户名 组名 大小 时间 文件名 目录操作&#xff1a;ls 库 进程 进程&#xff1a;创建进程 线程&#xff1a;创建线程…

36、Adaptive Forms(4)Create Adapt Form

文章目录 36、Adaptive Forms&#xff08;4&#xff09;Create Adapt FormData Model服务配置创建Adapt Form Template创建Adapt Form 36、Adaptive Forms&#xff08;4&#xff09;Create Adapt Form DataModel创建好后&#xff0c;就可以进行数据的获取和存储。 Data Model…

抖音seo源码开发/技术自研底层逻辑

抖音seo霸屏&#xff0c;是一种专为抖音视频创作者和传播者打造的视频批量剪辑&#xff0c;批量分发产品。使用抖音seo霸屏软件&#xff0c;可以帮助用户快速高效的制作出高质量的优质视频。 使用方法&#xff1a;1. 了解用户的行为习惯 2. 充分利用自身资源进行开发 3. 不…

开心档之Java 基本数据类型

Java 基本数据类型 目录 Java 基本数据类型 内置数据类型 实例 实例 类型默认值 实例 引用类型 Java 常量 自动类型转换 自动类型转换 实例 强制类型转换 实例 隐含强制类型转换 变量就是申请内存来存储值。也就是说&#xff0c;当创建变量的时候&#xff0c;需…

4.5.1 虚拟局域网(一)

4.5.1 虚拟局域网&#xff08;一&#xff09; 一、虚拟局域网的划分 虚拟局域网的划分是非常灵活的&#xff0c;可以根据端口进行划分&#xff0c;也可以根据MAC地址进行划分&#xff0c;也可以根据网络层协议进行划分甚至根据IP组播进行划分。 &#xff08;一&#xff09;基…

百度工程师移动开发避坑指南——内存泄漏篇

作者 | 启明星小组 在日常编写代码时难免会遇到各种各样的问题和坑&#xff0c;这些问题可能会影响我们的开发效率和代码质量&#xff0c;因此我们需要不断总结和学习&#xff0c;以避免这些问题的出现。接下来我们将围绕移动开发中常见问题做出总结&#xff0c;以提高大家的开…

【Prompting】ChatGPT Prompt Engineering开发指南(6)

ChatGPT Prompt Engineering开发指南&#xff1a;Expanding/The Chat Format Expanding自定义对客户电子邮件的自动回复提醒模型使用客户电子邮件中的详细信息 The Chat Format总结内容来源 在本教程中&#xff0c;第一部分学习生成客户服务电子邮件&#xff0c;这些电子邮件是…

前端(HTML)

网络传输三大基石:URL,HTTP,HTML 前端使用URL利用HTTP协议去向服务器端发送请求某个资源&#xff0c;服务器端响应浏览器一个HTML页面&#xff0c;浏览器对HTML页面解析 HTML的标准结构: 【1】先用普通文本文档新建一个文本&#xff0c;将文本的后缀改为.html 或者 .htm 我…

chatgpt赋能Python-mac版的python怎么用

Mac版Python的使用指南 Python是一种高级编程语言&#xff0c;常用于Web开发、数据分析、机器学习等领域。在Mac系统上&#xff0c;Python的安装和使用非常方便。本文将介绍如何在Mac上安装和使用Python并演示几个常见的Python用例。 Python在Mac上的安装 Mac电脑自带Python…