[LeetCode] 哈希表 I — 242#有效的字母异位词 | 349#两个数组的交集 | 202#快乐数 | 1#两数之和

news2025/1/20 20:18:51

哈希表

  • 基础知识
  • 常见的哈希结构
  • 数组
    • 242# 有效的字母异位词
  • Set
    • 基础语句
    • 349# 两个数组的交集
    • 202# 快乐数
  • Map
    • 基础语句
    • 1# 两数之和

基础知识

哈希表常用于快速判断一个元素是否在集合中,空间换时间

哈希表是根据key(如数组的索引下标)直接进行访问的数据结构

哈希函数:将key映射到哈希表上的索引 index = hashFunction(key) = (hashCode(key) % tableSize) mod tableSize

hashCode()通过特定编码方式把key转化为数值

哈希碰撞:不同的key引射到了相同的下标,解决方法:拉链法线性探测法

  • 拉链法:将发生冲突的元素存储在链表中,不需要tableSize大于dataSize

  • 线性探测法:向下找空位,需要tableSize一定大于dataSize

常见的哈希结构

  • 数组

​常用于key的数值十分受限,密度高的情况,相比起set时间和空间复杂度低(set把数值映射到key上要做hash计算)

  • set(集合)

常用于key较少、分散且数值跨度大的情况(使用数组会造成空间的极大浪费)

一般来说,当要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的;如果需要集合是有序的则用set;如果要求不仅有序还要有重复数据则用multiset

  • map(映射)

map 是一个key-value 的数据结构

map中对key是有限制,而对value没有限制,因为key的存储方式是用红黑树实现的

hash_sethash_mapunordered_setunordered_map的关系:

功能相同, 但unordered_setC++11被引入标准库,因此建议使用unordered_set

数组

常用于key的数值十分受限,密度高的情况,相比起set时间和空间复杂度低(set把数值映射到key上要做hash计算)

242# 有效的字母异位词

给定两个字符串 st ,编写一个函数来判断 t 是否是 s 的 字母异位词。

字母异位词是通过重新排列不同单词或短语的字母而形成的单词或短语,并使用所有原字母一次。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

提示:

  • 1 <= s.length, t.length <= 5 * 104
  • st 仅包含小写字母

由于字符串只有小写字母,因此定义一个长度为26的数组record记录字符串中字符出现的次数

// 哈希数组
// O(n) 0ms; O(1) 9.51MB
class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for (int i = 0; i < s.size(); i++) {
            record[s[i] - 'a']++;
        }
        for (int i = 0; i < t.size(); i++) {
            record[t[i] - 'a']--;
        }

        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) return false;
        }

        return true;
    }
};

空间复杂度:一个常量大小的辅助数组O(26),即 O(1) 或 O(S)

Set

常用于key较少、分散且数值跨度大的情况(使用数组会造成空间的极大浪费)

基础语句

// 创建
unordered_set<int> hashset;
unordered_set<int> hashset(vector.begin(), vector.end()); // 范围构造函数,参数为两个迭代器,将vector的所有元素插入hashset中,遍历O(n),插入所有元素到set的时间复杂度为O(n)~O(n^2)(哈希冲突严重)
// 查询
unordered_set.find(key)  // O(1) 如果key存在,返回一个指向该元素的迭代器;不存在返回特殊值unordered_set.end()
// 插入
unordered_set.insert(key)
// 长度
unordered_set.size()
// 桶数量
unordered_set.bucket_count() // unordered_set会动态扩容(rehashing)

349# 两个数组的交集

给定两个数组 nums1nums2 ,返回它们的 交集。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序

数组的交集:同时出现在数组中的元素的集合

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

提示:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 1000

唯一且无序,选择unordered_set

先将nums1转化为unordered_set,再取unordered_setnums2的交集

// unordered_set
// O(n+m) 8ms; O(n) 14.43MB
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> nums1_set(nums1.begin(), nums1.end());
        unordered_set<int> result_set; // 使用set为最终结果去重
        for (int num : nums2) {
            if (nums1_set.find(num) != nums1_set.end()) result_set.insert(num);
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

由于本题力扣对数组的数值进行了大幅限制,因此也可使用哈希数组

// 哈希数组 & unordered_set
// O(n+m) 4ms; O(n) 14.58MB
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        int hash[1001] = {0};
        for (int num : nums1) {
            hash[num] = 1;
        }
        for (int num : nums2) {
            if (hash[num] == 1) result_set.insert(num);
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

202# 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

输入:n = 19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1

示例 2:

输入:n = 2
输出:false

提示:

  • 1 <= n <= 2^31 - 1

该题的突破点在于无限循环(结果会收敛到1 或 无限循环)

无限循环的原因:对于一个非负整数,其每位平方和范围有限(如三位数的每位平方和最大为243)

// unordered_set
// O(logn) 0ms; O(logn) 8.25MB
class Solution {
public:
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set<int> hashset;
        while (n != 1) {
            if (hashset.find(n) != hashset.end()) return false;
            hashset.insert(n);
            n = getSum(n); 
        }
        return true;
    }
};

用集合记录可能导致集合过大,递归的层次较深也会导致调用栈崩溃

另一种方法是快慢指针法

每位平方和的过程是一个隐式链表,则转换为链表中是否有环的问题,fast走两步,slow走一步(相对速度为1),二者相等即存在环

由于该题的特殊性——为1时1自身形成环,因此只需判断fastslow相遇时(环中)是否为1

// 快慢指针法
// O(logn) 0ms; O(1) 7.6MB
class Solution {
public:
    int bitSquareSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow = n, fast = n;
        do {
            slow = bitSquareSum(slow);
            fast = bitSquareSum(bitSquareSum(fast));
        } while (slow != fast);
        return slow == 1;
    }
};

Map

基础语句

// 创建
unordered_map<int, int> hashmap;

// 查询
auto iter = unordered_map.find(key); // O(1),不存在返回 unordered_map.end()
iter->first // 查key
iter->second // 查value
auto iter = M.begin(); // 第一个元素
iter+1 // 下一个元素

// 插入
unordered_map[key] = value;
unordered_map.insert(pair<int, int>(key, value));

1# 两数之和

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

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

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

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

需要查询元素的同时,还需要元素对应的下标,因此用mapkey可以无需,因此用unordered_map

{key:数据元素,value:对应下标}

// unordered_map
// O(n) 3ms; O(n) 14.59MB
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashmap;
        for (int i = 0; i < nums.size(); i++) {
            auto iter = hashmap.find(target - nums[i]);
            if (iter != hashmap.end()) return {iter->second, i};
            hashmap[nums[i]] = i;
            // hashmap.insert(pair<int, int>(nums[i], i));
        }
        return {};
    }
};

本文参考了 LeetCode官方题解 及 代码随想录

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

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

相关文章

二、华为交换机 Trunk

一、Trunk功能 Trunk口主要用于连接交换机与交换机&#xff08;或路由器&#xff09;&#xff0c;允许在一条物理链路上传输多个VLAN的数据。这大大增加了网络的灵活性和可扩展性&#xff0c;使得不同VLAN之间的通信变得更加便捷。 二、作用原理 标签处理&#xff1a;Trunk口能…

基于SSM的自助购药小程序设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

AI守护煤矿安全生产:基于视频智能的煤矿管理系统架构解析

前言 本文我将介绍我和我的团队自主研发设计的一款AI产品的成果展示——“基于视频AI识别技术的煤矿安全生产管理系统”。 这款产品是目前我在创业阶段和几位矿业大学的博士共同从架构设计、开发到交付的全过程中首次在博客频道发布, 我之前一直想写但没有机会来整理这套系统的…

SpringCloud -根据服务名获取服务运行实例并进行负载均衡

Nacos注册中心 每个服务启动之后都要向注册中心发送服务注册请求&#xff0c;注册中心可以和各个注册客户端自定义协议实现服务注册和发现。 pom.xml <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-na…

LLM - 大模型 ScallingLaws 的 CLM 和 MLM 中不同系数(PLM) 教程(2)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145188660 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Scalin…

Android CustomTextField

在 Compose 中开发用户界面时&#xff0c;需要处理输入框和键盘的交互&#xff0c;例如在键盘弹出时调整布局位置&#xff0c;避免遮挡重要内容。本篇博客将通过一个完整的示例展示如何实现这一功能。 功能概述 本例实现了一个简单的输入框。当输入框获得焦点或输入文字时&…

【韩顺平Java笔记】第8章:面向对象编程(中级部分)【338-342】

338. 零钱通消费 package com.masterspark.smallchange;import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner;public class SmallChangeSys {public static void main(String[] args) {//1. 先完成显示菜单&#xff0c;并可以选择菜单&#…

Mac M1处理器uiautomatorviewer 使用

问题 Android自带工具uiautomatorviewer在mac电脑上运行报错 解决 有位大神解决了这个问题 项目网址&#xff1a;https://github.com/TarCV/uiautomatorviewer-gradle ./gradlew installDist JAVA_OPTS-XstartOnFirstThread ./build/install/uiautomatorviewer-gradle/bin…

【漫话机器学习系列】054.极值(Extrema)

极值&#xff08;Extrema&#xff09; 定义 极值是数学分析和优化问题中的一个核心概念&#xff0c;指函数在某个定义域内取得的最大值或最小值。根据极值的性质&#xff0c;可以将其分为两类&#xff1a; 局部极值&#xff08;Local Extrema&#xff09;&#xff1a;函数在…

QT开发技术 【基于TinyXml2的对类进行序列化和反序列化】一

一、对TinyXml2 进行封装 使用宏 实现序列化和反序列化 思路&#xff1a; 利用宏增加一个类函数&#xff0c;使用序列化器调用函数进行序列化 封装宏示例 #define XML_SERIALIZER_BEGIN(ClassName) \ public: \virtual void ToXml(XMLElement* parentElem, bool bSerialize …

代码随想录训练营第五十一天| 99.岛屿数量 深搜 岛屿数量 广搜 100.岛屿的最大面积

99.岛屿数量 深搜 题目链接&#xff1a;99. 岛屿数量 讲解链接&#xff1a;代码随想录 就是dfs模版题目 在dfs里可以先定义方向数组移动 再遍历分别向四个方向移动 同时记得更新当前nextx nexty 再判断是否越界 再执行判断条件 当前位置未走过 visited[i][j] false 一开始jav…

【HarmonyOS之旅】基于ArkTS开发(二) -> UI开发之常见布局

目录 1 -> 自适应布局 1.1 -> 线性布局 1.1.1 -> 线性布局的排列 1.1.2 -> 自适应拉伸 1.1.3 -> 自适应缩放 1.1.4 -> 定位能力 1.1.5 -> 自适应延伸 1.2 -> 层叠布局 1.2.1 -> 对齐方式 1.2.2 -> Z序控制 1.3 -> 弹性布局 1.3.1…

docker 部署 MantisBT

1. docker 安装MantisBT docker pull vimagick/mantisbt:latest 2.先运行实例&#xff0c;复制配置文件 docker run -p 8084:80 --name mantisbt -d vimagick/mantisbt:latest 3. 复制所需要配置文件到本地路径 docker cp mantisbt:/var/www/html/config/config_inc.php.…

【Linux系统编程】—— 深度解析进程等待与终止:系统高效运行的关键

文章目录 进程创建再次认识fork()函数fork()函数返回值 写时拷贝fork常规⽤法以及调用失败的原因 进程终⽌进程终止对应的三种情况进程常⻅退出⽅法_exit函数exit函数return退出 进程等待进程等待的必要性进程等待的⽅法 进程创建 再次认识fork()函数 fork函数初识&#xff1…

学习MyBatis的调优方案

MyBatis是一款优秀的Java持久层框架&#xff0c;它简化了数据库操作&#xff0c;并提供了灵活的SQL查询机制。然而&#xff0c;在实际应用中&#xff0c;我们可能会遇到一些性能问题&#xff0c;这时需要对MyBatis进行合理的调优。本文将详细探讨MyBatis的调优方案&#xff0c;…

python_在钉钉群@人员发送消息

python_在钉钉群人员发送消息 1、第一种 企业内部机器人群聊实现人接入指南&#xff0c;适用于群机器人接收消息&#xff0c;处理完一系列的动作之后&#xff0c;将消息返回给发消息的人员&#xff0c;同时该人员。 需要在企微后台新建一个自建应用&#xff0c;在自建应用里…

【Linux】进程优先级与进程切换

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;Linux &#x1f339;往期回顾&#x1f339;&#xff1a;【Linux】进程状态 &#x1f516;流水不争&#xff0c;争的是滔滔不 一、进程优先级是什么二、查看系统进程三…

imbinarize函数用法详解与示例

一、函数概述 众所周知&#xff0c;im2bw函数可以将灰度图像转换为二值图像。但MATLAB中还有一个imbinarize函数可以将灰度图像转换为二值图像。imbinarize函数是MATLAB图像处理工具箱中用于将灰度图像或体数据二值化的工具。它可以通过全局或自适应阈值方法将灰度图像转换为二…

电商项目高级篇08-springCache

电商项目高级篇08-springCache 1、整合springCache2、Cacheable细节设置 1、整合springCache 1、引入依赖 <!--引入springCache--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifa…

macOS 安装JDK17

文章目录 前言介绍新特性下载安装1.下载完成后打开downloads 双击进行安装2.配置环境变量3.测试快速切换JDK 小结 前言 近期找开源软件&#xff0c;发现很多都已经使用JDK17springboot3 了&#xff0c;之前的JDK8已经被替换下场&#xff0c;所以今天就在本机安装了JDK17&#…