C++算法训练营 Day6 哈希表(1)

news2025/6/6 5:59:41

1.有效的字母异位词

  • LeetCode:242.有效的字母异位词

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

示例 1:

输入: s = “anagram”, t = “nagaram”
输出: true

示例 2:

输入: s = “rat”, t = “car”
输出: false

  • 解题思路:

由题可知,字母异位词是两个数组字母相同但是顺序不同,我们需要快速的找出某个值是否在表中,因此考虑使用哈希表。

1.使用一个大小为26的数组(对应26个英文字母)作为计数器;
2.遍历第一个字符串,将出现的字母对应增加相应字符的计数;
3.遍历第二个字符串,将出现的字母减少相应字符的计数;
4.如果两个字符串是字母异位词,最终计数器所有位置都应为0

其中创建一个包含26个整数的数组(初始化为0),每个位置对应一个字母,如:

res[0] → ‘a’ 的计数
res[1] → ‘b’ 的计数

res[25] → ‘z’ 的计数

然后将字符到索引利用一个巧妙的ASCII码进行转换s[i] - 'a',如:

‘a’ 的ASCII码是97
‘b’ 是98,
…,
‘z’ 是122

然后通过计算可得:

‘a’ - ‘a’ = 0
‘b’ - ‘a’ = 1

‘z’ - ‘a’ = 25

从而就得到了每个字母的对应位置。由此便可进行计算

如:
(1)有效字母异位词

s = “anagram”, t = “nagaram”

1.遍历s: a→+1, n→+1, a→+1, g→+1, r→+1, a→+1, m→+1
2.遍历t: n→-1, a→-1, g→-1,a→-1, r→-1, a→-1, m→-1
3.所有计数器归零 → 返回true

(2)无效字母异位词

s = “rat”, t = “car”

1.遍历s: r→+1, a→+1, t→+1
2.遍历t: c→-1, a→-1, r→-1
3.最终计数器中: a:0, r:0, t:+1,c:-1
t,c是非零值 ,因此返回false

class Solution {
public:
    bool isAnagram(string s, string t) {
        //步骤1:创建计数器数组
        int res[26] = {0};
        
        //步骤2:遍历字符串s,增加字符计数
        for(int i = 0; i < s.size(); ++i) 
            res[s[i] - 'a']++;
        
        //步骤3:遍历字符串t,减少字符计数
        for(int i = 0; i < t.size(); ++i) 
            res[t[i] - 'a']--;
        
        //步骤4:检查计数器是否全为0
        for(int i = 0; i < 26; ++i) {
            if(res[i] != 0) 
                return false;  //任何非零值都表示计数不匹配
        }
        return true;  //所有计数均为0,是字母异位词
    }
};

2.两个数组的交集

  • LeetCode: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] 也是可通过的

  • 解题思路:

本题要求我们找出两个数组中的相同元素,涉及到快速查找某个元素是否在表中,因此考虑用哈希表来求解,此外若题目限制了数组的大小可以选择数组来解题,但是本道题没有规定数组的大小,因此无法用数组来做哈希表,而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。因此我们考虑使用set或map,对于本题来说,我们只需返回相同的元素即可,且不要求顺序,因此我们使用unordered_set求解。

首先unordered_set中数值要求不能重复,因此会自动过滤重复元素,我们可以创建一个结果集用来存放交集元素,同时将数组nums1转换为哈希表,即:

[1,2,2,1] → {1,2}

具体操作为:

  	unordered_set<int> result_set; //存储结果的集合(自动去重)
    unordered_set<int> nums_set(nums1.begin(), nums1.end()); 
    //将nums1转换为哈希集合(自动去重)

然后我们遍历nums2,同时在哈希表nums1中寻找nums2中有的元素,若找着了,就把这个元素插入到结果集中即:

for (int num : nums2) {
        //检查当前元素是否存在于nums1的集合中
        if (nums_set.find(num) != nums_set.end()) {
            result_set.insert(num);  //存在则加入结果集
        }
    }

对于int num:是声明了一个临时变量num,类型是int,每次循环时,num会被自动赋值为nums2的当前元素。注意:这里是值拷贝,即复制元素值nums2:则是要遍历的容器,这里是vector。编译器会自动处理迭代过程。即执行流程为:

第一次循环:num = nums2的第一个元素
第二次循环:num = nums2的第二个元素

直到遍历完nums2中所有元素

if (nums_set.find(num) != nums_set.end()) 是判断元素num是否存在于集合nums_set中的标准方法。

nums_set.find(num):在集合中查找键为num的元素

若找到:返回指向该元素的迭代器
若未找到:返回一个特殊的迭代器nums_set.end()(表示集合末尾的下一个位置,为无效位置)

最后将结果集转化为vector返回,即:

  return vector<int>(result_set.begin(), result_set.end());

整体C++代码为:

vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
    //存储结果的集合(自动去重)
    unordered_set<int> result_set; 
    
    //将nums1转换为哈希集合(自动去重)
    unordered_set<int> nums_set(nums1.begin(), nums1.end());
    
    //遍历nums2中的每个元素
    for (int num : nums2) {
        //检查当前元素是否存在于nums1的集合中
        if (nums_set.find(num) != nums_set.end()) {
            result_set.insert(num);  //存在则加入结果集
        }
    }
    
    //将结果集合转换为vector返回
    return vector<int>(result_set.begin(), result_set.end());
}

3.快乐数

  • LeetCode:202.快乐数

编写一个算法来判断一个数 n 是不是快乐数。「快乐数」 定义为:

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

示例 1:

在这里插入图片描述

示例 2:

在这里插入图片描述

-解题思路:

由题意,对于一个正整数,重复将其替换为各位数字的平方和,最终结果变为1的数即为快乐数。而无限循环时不是快乐数,而在无限循环的过程中,一定会出现重复的结果,因此我们需要在结果集中快速寻找是否存在重复的结果,若存在即陷入循环了,就不是循环数。此外对结果的顺序不做要求,因此选择unordered_set。

首先,我们需要计算给定n的各位平方和,即:

 //计算数字各位平方和的辅助函数
    int getsum(int n) {
        int sum = 0;
        while(n) {  //当n不为0时继续处理
            sum += (n % 10) * (n % 10);  //取最后一位数字并平方
            n /= 10;  //移除最后一位数字
        }
        return sum;
    }

这是一个基本的处理一个整数各个位的方法,如:

初始:n=19
第一次循环:19 % 10 = 9, 则sum = 81, 19 / 10 = 1,即n更新为1
第二次循环:1 % 10 = 1, sum = 81 + 1 = 82, 1 / 10 = 0
不满足循环条件,返回82

然后,我们需要创建一个结果集,用来存放所有sum。并使用哈希集合检测循环,若结果等于1,则说明为快乐数;若结果重复出现,则说明非快乐数。

  unordered_set<int> res;  // 储出现过的计算结果
        while(1) {  //无限循环直到找到结果
            int sum = getsum(n);  //计算当前数字的平方和
            
            //检查是否满足快乐数条件
            if(sum == 1) 
                return true;  
            
            //检查是否出现循环(结果重复)
            if(res.find(sum) != res.end()) 
                return false;
            else 
                res.insert(sum);  //记录新结果
            
            n = sum;  //更新n为新的计算结果,确保每次迭代使用最新计算结果
        }

整体代码为:

class Solution {
public:
    int getsum(int n) {
        int sum = 0;
        while(n) {  //当n不为0时继续处理
            sum += (n % 10) * (n % 10);  //取最后一位数字并平方
            n /= 10;  //移除最后一位数字
        }
        return sum;
    }
    
    //判断是否为快乐数
    bool isHappy(int n) {
        unordered_set<int> res;  //存储出现过的计算结果
        while(1) {  // 无限循环直到找到结果
            int sum = getsum(n);  //计算当前数字的平方和
            
            //检查是否满足快乐数条件
            if(sum == 1) 
                return true;  
            
            //检查是否出现循环(结果重复)
            if(res.find(sum) != res.end()) 
                return false;
            else 
                res.insert(sum);  //记录新结果
            
            n = sum;  //更新n为新的计算结果
        }
    }
};

4.两数之和

  • LeetCode: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]

  • 解题思路

本题不是直接找两个数,而是对于每个元素nums[i],检查target - nums[i]是否存在于之前遍历过的元素中,其思路与有效字母异位词的思路一致。即如果a + b = target
那么b = target - a。因此我们可以使用哈希表:使用unordered_map存储已遍历元素的值和索引,通过一次遍历,边遍历边检查边存储。

首先建立结果集:

unordered_map<int,int> res//键:元素值,值:元素索引

key用来存储数组元素的值,用于快速查找;value用来存储该元素的索引,用于最终返回结果。

然后通过遍历结果集查找是否存在target - a,若存在返回下标,若不存在返回空。即整体流程为:

假设我们输入:

nums = [2, 7, 11, 15], target = 9

执行流程:

第一次循环:i=0,nums[0]=2
结果集中查找9-2=7(不存在)
插入map: {2:0}
第二次循环: i=1,nums[1]=7
结果集中查找9-7=2(找到,索引为0)
返回[0,1]

for(int i = 0; i < nums.size(); i++) {
        // 尝试寻找能与nums[i]配对的数
        auto iter = map.find(target - nums[i]);
        
        // 如果找到配对
        if(iter != map.end()) {
            return {iter->second, i};  // 返回两个索引
        }
        
        // 将当前元素存入map(先检查后存储,避免自匹配)
        map.insert(pair<int, int>(nums[i], i)); 
    }
return { };

整体代码为:

vector<int> twoSum(vector<int>& nums, int target) {
    std::unordered_map<int,int> res;  //键:元素值,值:元素索引
    for(int i = 0; i < nums.size(); i++) {
        //尝试寻找能与nums[i]配对的数
        auto it = res.find(target - nums[i]);
        
        //如果找到配对
        if(it != res.end()) {
            return {it->second, i};  //返回两个索引
        }  
        //将当前元素存入map(先检查后存储,避免自匹配)
        res.insert(pair<int, int>(nums[i], i)); 
    }
    return {};  
}

pair<int, int>pair为是C++标准库中的模板类,用于存储两个值作为一个单元。这里相当于创建了一个临时对象,包含:first = nums[i]second = i。当插入元素时,必须同时提供keyvalue,而pair正好把这两个值打包成一个对象。

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

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

相关文章

【C语言编译与链接】--翻译环境和运行环境,预处理,编译,汇编,链接

目录 一.翻译环境和运行环境 二.翻译环境 2.1--预处理(预编译) 2.2--编译 2.2.1--词法分析 2.2.2--语法分析 2.2.3--语义分析 2.3--汇编 2.4--链接 三.运行环境 &#x1f525;个人主页&#xff1a;草莓熊Lotso的个人主页 &#x1f3ac;作者简介&#xff1a;C研发…

【JavaEE】多线程

8.线程状态 根据 Java 的Thread.state包&#xff0c;线程一共有六种状态&#xff1a; NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED 二、每种状态的含义 1. NEW&#xff08;新建&#xff09; 当使用new 关键字创建一个线程对象&#xff0c;但尚未调用其start() 方法时…

【项目】在线OJ(负载均衡式)

目录 一、项目目标 二、开发环境 1.技术栈 2.开发环境 三、项目树 目录结构 功能逻辑 编写思路 四、编码 1.complie_server 服务功能 代码蓝图 开发编译功能 日志功能 ​编辑 测试编译模块 开发运行功能 设置运行限制 jsoncpp 编写CR 如何生成唯一文件名 …

贪心算法应用:在线租赁问题详解

贪心算法应用&#xff1a;在线租赁问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;从而希望导致结果是全局最优的算法策略。在线租赁问题(Greedy Algorithm for Online Rentals)是一个经典的贪心算法应用场景&#xff0c;下面我将从多个维度全面…

Prj10--8088单板机C语言8259测试(1)

1.原理图 2.Deepseek示例代码 #include <dos.h> #include <conio.h> #include <stdio.h>#define PIC1_CMD 0x400 // 命令端口 (A00) #define PIC1_DATA 0x401 // 数据端口 (A01)volatile int int_count 0; // 中断计数器 void interrupt (*old_isr)(…

3步在小米13手机跑DeepSeek R1

大家好&#xff01;我是羊仔&#xff0c;专注AI工具、智能体、编程。 一、从性能旗舰到AI主机 春节大扫除时&#xff0c;翻出尘封的小米13&#xff0c;这台曾以骁龙8 Gen2著称的性能小钢炮&#xff0c;如今正在执行更科幻的使命——本地运行DeepSeek R1。 想起两年前用它连续肝…

注销微软账户

因为我的微软开发者账户丢失 Office E5 权限&#xff0c;因此需要注销。 若你需要注销微软账号&#xff0c;请点击下方超链接。 点击此处 注销之后仅剩一个正常的账户使用咯&#xff01;&#xff01;

Ubuntu 服务器软件更新,以及常用软件安装 —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录 3

前言 前面&#xff0c;我们已经 安装好了 Ubuntu 服务器系统&#xff0c;并且 配置好了 ssh 免密登录服务器 &#xff0c;现在&#xff0c;我们要来进一步的设置服务器。 那么&#xff0c;本文&#xff0c;就是进行服务器的系统更新&#xff0c;以及常用软件的安装 调整 Ubu…

飞牛NAS+Docker技术搭建个人博客站:公网远程部署实战指南

文章目录 前言1. Docker下载源设置2. Docker下载WordPress3. Docker部署Mysql数据库4. WordPress 参数设置5. 飞牛云安装Cpolar工具6. 固定Cpolar公网地址7. 修改WordPress配置文件8. 公网域名访问WordPress总结 前言 在数字化浪潮中&#xff0c;传统网站搭建方式正面临前所未…

刷leetcode hot100返航必胜版--链表6/3

链表初始知识 链表种类&#xff1a;单链表&#xff0c;双链表&#xff0c;循环链表 链表初始化 struct ListNode{ int val; ListNode* next; ListNode(int x): val&#xff08;x&#xff09;,next(nullptr) {} }; //初始化 ListNode* head new ListNode(5); 删除节点、添加…

C# 序列化技术全面解析:原理、实现与应用场景

在软件开发中&#xff0c;数据持久化和网络通信是两个至关重要的环节。想象一下&#xff0c;当我们需要将一个复杂的对象保存到文件中&#xff0c;或者通过网络发送到另一台计算机时&#xff0c;如何有效地表示这个对象&#xff1f;这就是序列化技术要解决的问题。序列化&#…

electron定时任务,打印内存占用情况

// 监听更新 function winUpdate(){// 每次执行完后重新设置定时器try {// 获取当前时间并格式化为易读的字符串const now new Date();const timeString now.toLocaleString();console.log(当前时间: ${timeString});// 记录内存使用情况&#xff08;可选&#xff09;const m…

Gitee Wiki:以知识管理赋能 DevSecOps,推动关键领域软件自主演进

关键领域软件研发中的知识管理困境 传统文档管理模式问题显著 关键领域软件研发领域&#xff0c;传统文档管理模式问题显著&#xff1a;文档存储无系统&#xff0c;查找困难&#xff0c;降低效率&#xff1b;更新不及时&#xff0c;与实际脱节&#xff0c;误导开发&#xff1…

学习STC51单片机24(芯片为STC89C52RCRC)

每日一言 把 “我不行” 换成 “我试试”&#xff0c;你会发现一片新的天地。 那关于优化 白盒测试 我们之前不是通过这个接线方式可以看到返回到信息嘛因为安信可的特性就是返回Esp8266的反馈&#xff0c;可以看到代码死在哪里了&#xff0c;导致连接不上&#xff0c;因为我们…

LabVIEW基于 DataSocket从 OPC 服务器读取数据

LabVIEW 中基于 DataSocket 函数从 OPC 服务器读取数据的功能&#xff0c;为工业自动化等场景下的数据交互提供了解决方案。通过特定函数实现 URL 指定、连接建立与管理、数据读取&#xff0c;相比传统 Socket 通信和 RESTful API &#xff0c;在 OPC 服务器数据交互场景有适配…

阿里云无影云桌面深度测评

阿里云无影桌面深度测评&#xff1a;解锁云端工作“新范式”的“未来之钥”&#xff01; 在数字化浪潮席卷全球的2025年&#xff0c;远程办公与混合办公已不再是权宜之计&#xff0c;而是职场不可逆转的新常态。然而&#xff0c;如何确保员工无论身在何处&#xff0c;都能拥有…

深入浅出:Oracle 数据库 SQL 执行计划查看详解(1)——基础概念与查看方式

背景 在当今的软件开发领域&#xff0c;尽管主流开发模式往往倾向于采用单表模式&#xff0c;力图尽可能地减少表之间的连接操作&#xff0c;以期达到提高数据处理效率、简化应用逻辑等目的。然而&#xff0c;对于那些已经上线运行多年的运维老系统而言&#xff0c;它们内部往…

前端​​HTML contenteditable 属性使用指南

​​什么是 contenteditable&#xff1f; HTML5 提供的全局属性&#xff0c;使元素内容可编辑类似于简易富文本编辑器兼容性​​ 支持所有现代浏览器&#xff08;Chrome、Firefox、Safari、Edge&#xff09; 移动端&#xff08;iOS/Android&#xff09;部分键盘行为需测试 &l…

自动化采集脚本与隧道IP防封设计

最近群里讨论问如何编写一个自动化采集脚本&#xff0c;要求使用隧道IP&#xff08;代理IP池&#xff09;来防止IP被封。这样的脚本通常用于爬虫或数据采集任务&#xff0c;其中目标网站可能会因为频繁的请求而封禁IP。对于这些我还是有些经验的。 核心思路&#xff1a; 1、使…

【设计模式-4.7】行为型——备忘录模式

说明&#xff1a;本文介绍行为型设计模式之一的备忘录模式 定义 备忘录模式&#xff08;Memento Pattern&#xff09;又叫作快照模式&#xff08;Snapshot Pattern&#xff09;或令牌模式&#xff08;Token Pattern&#xff09;指在不破坏封装的前提下&#xff0c;捕获一个对…