Leetcode---1.两数之和 (详解加哈希表解释和使用)

news2024/12/22 18:01:36

文章目录

    • 题目 [两数之和](https://leetcode.cn/problems/two-sum/)
      • 方法一:暴力枚举
      • 代码
      • 方法二:哈希表
      • 代码
    • 哈希表
      • 哈希表的基本概念
        • 哈希函数(Hash Function):
        • 冲突(Collision):
        • 链地址法(Chaining):
        • 开放地址法(Open Addressing):
      • 哈希表的操作
        • 插入(Insert):
        • 查找(Search):
        • 删除(Delete):
      • 哈希表的优点和缺点
        • 优点:
        • 缺点:
      • 基本用法
      • 示例代码
        • 示例 1:计数字符出现次数
        • 示例 2:两数之和问题
      • 注意事项
        • 性能
        • 哈希函数
        • 内存开销

题目 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

在这里插入图片描述

方法一:暴力枚举

思路及算法

最容易想到的方法是枚举数组中的每一个数 x,寻找数组中是否存在 target - x。

当我们使用遍历整个数组的方式寻找 target - x 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x。

代码

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (nums[i] + nums[j] == target) {
                    return {i, j};
                }
            }
        }
        return {};
    }
};

复杂度分析

时间复杂度:O(N2),其中 N 是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。

空间复杂度:O(1).

方法二:哈希表

思路及算法

注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。

使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N) 降低到 O(1)。

这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

代码

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i;
        }
        return {};
    }
};

复杂度分析

时间复杂度:O(N),其中 N 是数组中的元素数量。对于每一个元素 x,我们可以 O(1) 地寻找 target - x。

空间复杂度:O(N),其中 N 是数组中的元素数量。主要为哈希表的开销。

哈希表

哈希表的基本概念

哈希函数(Hash Function):
  1. 哈希函数将输入的键转换为哈希码,这个哈希码通常是一个整数。
  2. 哈希码用于确定键值对在哈希表中的存储位置。
  3. 一个好的哈希函数应当均匀地分布键,减少冲突的发生。
冲突(Collision):
  1. 当两个不同的键被哈希函数映射到同一个存储位置时,称为冲突。
  2. 处理冲突的方法主要有两种:链地址法(Chaining)和开放地址法(Open Addressing)。
链地址法(Chaining):
  1. 每个存储桶存储一个链表或其他数据结构来处理冲突。
  2. 当冲突发生时,新键值对被插入到对应存储桶的链表中。
开放地址法(Open Addressing):
  1. 当冲突发生时,寻找另一个空的存储桶来存储冲突的键值对。
  2. 常见的开放地址法包括线性探测(Linear Probing)、二次探测(Quadratic Probing)和双重散列(Double Hashing)。

哈希表的操作

插入(Insert):
  1. 计算键的哈希码,找到对应的存储桶。
  2. 如果没有冲突,直接插入。
  3. 如果有冲突,根据冲突解决策略进行处理(例如链地址法或开放地址法)。
查找(Search):
  1. 计算键的哈希码,找到对应的存储桶。
  2. 在存储桶中查找键值对。
  3. 如果使用链地址法,可能需要遍历链表。
删除(Delete):
  1. 计算键的哈希码,找到对应的存储桶。
  2. 在存储桶中查找并删除键值对。
  3. 如果使用链地址法,可能需要遍历链表。

哈希表的优点和缺点

优点:
  1. 快速查找、插入和删除: 平均情况下,这些操作的时间复杂度都是 O(1)。
  2. 实现简单: 相对于平衡树等复杂数据结构,哈希表的实现较为简单。
缺点:
  1. 需要处理冲突: 尽管冲突不可避免,但冲突处理机制(如链地址法或开放地址法)会影响性能。
  2. 内存消耗: 哈希表通常需要额外的内存来存储链表或解决冲突,这在存储空间有限的情况下可能是个问题。
  3. 无法有序遍历: 哈希表中的元素没有特定顺序,因此不能按顺序遍历所有键值对。

基本用法

在C++中,unordered_map 是标准库(STL)中的一种关联容器,提供了键值对的哈希表实现。下面是一些常见的操作:

  1. 引入头文件
#include <unordered_map>
  • 在使用 unordered_map 之前,需要引入 <unordered_map> 头文件。
  1. 声明一个哈希表
std::unordered_map<int, int> hashtable;
  • 声明一个键为 int,值也为 int 的哈希表。
  1. 插入元素
hashtable[1] = 100;
hashtable.insert({2, 200});
  • 使用 [] 操作符或 insert 方法插入键值对。
  1. 访问元素
int value = hashtable[1];
auto it = hashtable.find(2);
if (it != hashtable.end()) {
    std::cout << "Found: " << it->second << std::endl;
}
  • 使用 [] 操作符访问元素或使用 find 方法查找元素。
  1. 删除元素
hashtable.erase(1);
  • 使用 erase 方法删除键值对。
  1. 遍历哈希表
for (const auto& pair : hashtable) {
    std::cout << pair.first << ": " << pair.second << std::endl;
}

使用范围 for 循环遍历哈希表中的所有键值对。

示例代码

下面是一些具体示例,展示如何使用 unordered_map:

示例 1:计数字符出现次数
#include <unordered_map>
#include <iostream>
#include <string>

int main() {
    std::string str = "hello world";
    std::unordered_map<char, int> char_count;

    // 统计每个字符出现的次数
    for (char c : str) {
        if (c != ' ') {
            char_count[c]++;
        }
    }

    // 输出字符出现的次数
    for (const auto& pair : char_count) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}
示例 2:两数之和问题
#include <unordered_map>
#include <vector>
#include <iostream>

std::vector<int> twoSum(const std::vector<int>& nums, int target) {
    std::unordered_map<int, int> hashtable;
    for (int i = 0; i < nums.size(); ++i) {
        int complement = target - nums[i];
        auto it = hashtable.find(complement);
        if (it != hashtable.end()) {
            return {it->second, i};
        }
        hashtable[nums[i]] = i;
    }
    return {};
}

int main() {
    std::vector<int> nums = {2, 7, 11, 15};
    int target = 9;
    std::vector<int> result = twoSum(nums, target);
    if (!result.empty()) {
        std::cout << "Indices: " << result[0] << ", " << result[1] << std::endl;
    } else {
        std::cout << "No solution found." << std::endl;
    }
    return 0;
}

注意事项

性能
  1. unordered_map 提供平均 O(1) 的插入、查找和删除操作,但在最坏情况下,这些操作可能是 O(n) 的。
  2. 哈希表的性能高度依赖于哈希函数的质量和负载因子(元素数量与桶的数量之比)。
哈希函数
  1. 自定义类型作为键时,需要提供自定义的哈希函数和相等函数。
内存开销
  1. unordered_map 在存储键值对时会使用额外的内存来维护哈希桶。

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

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

相关文章

mysql 查询---多表设计

部分数据 1distinct去重 select distinct job from tb_emp;select * from tb_emp where id in (1,2,3); select * from tb_emp where id between 1 and 5; select * from tb_emp where name like __; #下划线匹配单个字符, %匹配任意多个字符select min(entrydate) from tb_e…

JUnit5参数化用例(三)

JUnit5枚举参数的参数化&#xff1a; 使用枚举类作为测试数据枚举参数参数化注解EnumSource必须与ParameterizedTest结合使用 枚举参数化注解 -简单使用&#xff1a; 需要添加EnumSource注解测试方法传入枚举类作为参数 在执行前&#xff0c;我们需了解enum枚举的使用方式&…

LLama3大模型本地部署 仅需6步完成对话模型本地安装部署。附送可视化ui安装、自定义模型目录,修改模型保存地址,第三方微调模型、中文模型下载地址

本篇分为三部分 一&#xff1a;6步完成llama3大模型本地部署 二&#xff1a;8步完成llama3可视化对话界面安装 三&#xff1a;重设模型文件路径 四&#xff1a;微调模型、中文模型下载资源分享 一、LLama3 大模型本地部署安装 首先去mata官网下载ollama客户端 Ollama 选择合适…

线程池 ThreadPool

一般情况下我们都使用Thread类创建线程&#xff0c;因为通过Thread对象可以对线程进行灵活 的控制。但过多创建线程和销毁线程&#xff0c;会消耗掉大量的内存和CPU资源&#xff0c; 假如某段时间内突然爆发了100个短小的线程&#xff0c;创建和销毁这些线程就会消耗很多时间&a…

如何使用一段传输线表示电感和电容

文中部分图片来自于《complete Wireless design》 如何使用一段传输线来表示电感和电容&#xff0c;本文将就此内容展开&#xff1a;

Redis-如何保证与Mysql数据一致性

文章目录 Redis与Mysql数据一致性的情况有哪些&#xff1f;Redis与Mysql数据保持一致性的方案&#xff1f;同步双写机制删除缓存重新加载机制延迟双删机制利用MQ保持数据一致性 本篇小结 更多相关内容可查看 Redis与Mysql数据一致性的情况有哪些&#xff1f; Redis和MySQL是两…

vue3 自定义组件

在项目中&#xff0c;我们会遇到一些没有现成的组件&#xff0c;那这个时候我们就需要自己去写一个满足我们需求的组件。 比如&#xff0c;我需要一个上下排布&#xff0c;上面显示标题&#xff0c;下面显示内容的组件。封装完成后方便复用。 1、布局组件 我定义一个上下结构的…

Git使用(4):分支管理

一、新建分支 首先选择Git -> Branches... 然后选择 New Branch&#xff0c;输入新分支名称&#xff0c;例如dev。 可以看到右下角显示已经切换到新建的dev分支了。 push到远程仓库&#xff0c;可以看到新添加的分支。 二、切换分支与合并分支 为了演示合并分支&#xff0c…

Linux(七) 动静态库

目录 一、动静态库的概念 二、静态库的打包与使用 2.1 静态库的打包 2.2 静态库的使用 三、动态库的打包与使用 3.1 动态库的打包 3.2 动态库的使用 3.3 运行动态库的四种方法 四、总makefile 一、动静态库的概念 静态库&#xff1a; Linux下&#xff0c;以.a为后缀的…

Python——IO编程

IO在计算机中指Input/Output&#xff0c;也就是输入和输出。由于程序和运行时数据是在内存中驻留&#xff0c;由CPU这个超快的计算核心来执行&#xff0c;涉及到数据交换的地方&#xff0c;通常是磁盘、网络等&#xff0c;就需要IO接口。 比如你打开浏览器&#xff0c;访问新浪…

蓝桥杯 EDA 组 历届国赛真题解析

一、2021年国赛真题 1.1 CN3767 太阳能充电电路 CN3767 是具有太阳能电池最大功率点跟踪功能的 4A&#xff0c;12V 铅酸电池充电管理集成电路。 最大功率点应指的是电池板的输出电压&#xff0c;跟踪电压其做保护。当然 CN3767 也可以直接使用直流充电&#xff0c;具体可以阅读…

污水处理环保设备厂商怎么选

在选择污水处理环保设备厂商时&#xff0c;需要综合考虑多个因素来确保选取的供应商能够提供高质量的设备和服务。以下是一些主要的考虑因素&#xff1a; 企业资质和认证&#xff1a;首先检查供应商是否拥有相关的资质证书和行业认证&#xff0c;例如ISO 9001质量管理体系认证、…

0基础安装 composer

解决&#xff1a; composer 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 php composer.phar可以运行 安装环境&#xff1a;系统w11 官网地址&#xff1a;Composer 1.安装composer 1.1打开命令行窗口 在命令行窗口里&#xff0c;右键是粘贴&#xff0…

Java | Leetcode Java题解之第92题反转链表II

题目&#xff1a; 题解&#xff1a; class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {// 设置 dummyNode 是这一类问题的一般做法ListNode dummyNode new ListNode(-1);dummyNode.next head;ListNode pre dummyNode;for (int i 0; …

【数组的度】leetcode,python

一种很菜的做法&#xff08;暴力&#xff09;&#xff0c;for循环&#xff08;样例能过一大半呢&#xff0c;复杂度的话。。。&#xff09; class Solution:def findShortestSubArray(self, nums: List[int]) -> int:nlen(nums)if n1:return nmx1#出现次数最多的计算for i …

Google:站长移除无效网址

当您的网址不需要呈现在Google站长中时&#xff0c;您可以在站长工具中移除网址 操作步骤&#xff1a;登录Google站长&#xff0c;绑定网站完成后&#xff0c;点击左侧删除 >> 输入网址 如果遇到一些网址&#xff0c;可以找寻网址间的规律&#xff0c;比如说&#xff0…

【oracle】图片转为字节、base64编码等形式批量插入oracle数据库并查询

1.熟悉、梳理、总结下Oracle相关知识体系 2.欢迎批评指正&#xff0c;跪谢一键三连&#xff01; 资源下载&#xff1a; oci.dll、oraocci11.dll、oraociei11.dll3个资源文件资源下载&#xff1a; Instant Client Setup.exe资源下载&#xff1a; oci.dll、oraocci11.dll、oraoc…

java代码混淆工具ProGuard混淆插件

java代码混淆工具ProGuard混淆插件 介绍 ProGuard是一个纯java编写的混淆工具&#xff0c;有客户端跟jar包两种使用方式。可以将程序打包为jar&#xff0c;然后用工具进行混淆&#xff0c;也可以在maven中导入ProGuard的插件&#xff0c;对代码进行混淆。 大家都知道 java代…

【SQL】SQL常见面试题总结(1)

目录 1、检索数据1.1、从 Customers 表中检索所有的 ID1.2、检索并列出已订购产品的清单1.2、检索所有列 2、排序检索数据2.1、检索顾客名称并且排序2.2、对顾客 ID 和日期排序2.3、按照数量和价格排序2.4、检查 SQL 语句 3、过滤数据3.1、返回固定价格的产品3.2、返回产品并且…

基于Vue和uni-app的增强型单选ccRadioView组件开发

标题&#xff1a;基于Vue和uni-app的增强单选组件ccRadioView的设计与实现 摘要&#xff1a;本文将详细介绍如何使用Vue和uni-app构建一个简单、好用且通用的单选框组件ccRadioView。该组件提供了单选列表的功能&#xff0c;并支持反向传值&#xff0c;方便开发者快速实现单选…