哈希表9.24

news2025/1/12 1:03:34

13.罗马数字转整数

13. 罗马数字转整数 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/roman-to-integer/?envType=list&envId=sxrVTWKy目的是将一串罗马数字字符串转为整数

使用哈希存储罗马字母对应的数字可以很方便我们遍历字符串时快速找到对应的数字,值得注意的是,某些字母在组合在一起时候,得到的结果应该是右边对应数字减去左边数字,那该怎么样确定这样的组合呢?
两种方法:
一种是if把所有组合都列出来,这种方法当然不推荐!
第二种是把这两个字母都放到哈希表中对比,发现规律,如果右边数较大时候,那么减去当前左边的数。
那么如果出现IM这样的怎么办呢?
不会出现的,这样的序列不符合罗马字母规范
也就是说在规范内,只有直接加当前字母和减去当前字母(特定组合时)两种可能

class Solution {
public:
    int romanToInt(string s) {
        unordered_map<char,int>map={{'I',1},{'V',5},{'X',10},{'L',50},{'C',100},
        {'D',500},{'M',1000}};int res=0;
        for(int i=0;i<s.size();++i){
            if(i+1<s.size()&&map[s[i]]<map[s[i+1]])res-=map[s[i]];
           else res+=map[s[i]];
           
        }
        return res;
    }
};

 没记错应该是官方题解,还是网友的,反正不是自己写的,代码思路和上面说的一样

实现细节就是,如果当前下标+1在数组范围内,那就和当前的数字一起进行判断,下一个数大于当前数字,那么我们就减去当前数字,因为通常都是左边字母对应数字大于右边,如果相反,那么说明一定是特殊组合


12.整数转罗马数字

12. 整数转罗马数字 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/integer-to-roman/?envType=list&envId=sxrVTWKy这道题是自己解出来的,这道题目用了两个哈希表

自写题解
用一个map来存储数字和罗马字母的对应关系,把可能出现的字符相反的也一并写出来
用一个vector来记录所有罗马字母能表示出来的数值从大到小排列它们
循环判断在num还没有等于0之前,里面用另一个循环判断,它最大能减去多少数字,把这个数字减去,并把对应的罗马字母加上
剪枝:由于num越减越小所以,下一次进来判断从哪个数减起,直接从上一次遍历的那个数开始就可以了,当前num不可能剪掉比这个数还大的数

class Solution {
public:
    string intToRoman(int num) {
        unordered_map<int,string>map={{1,"I"},{4,"IV"},{5,"V"},{9,"IX"},{10,"X"},
        {40,"XL"},{50,"L"},{90,"XC"},{100,"C"},{400,"CD"},{500,"D"},{900,"CM"},
        {1000,"M"}};
        vector<int>arr={1000,900,500,400,100,90,50,40,10,9,5,4,1};
        string s;int i=0;
        while(num){
            for(;i<arr.size();){
                if(num-arr[i]>=0){
                    s+=map[arr[i]];num-=arr[i];
                }
                else i++;
            }
        }
        return s;
    }
};

代码写得有点冗余,官方题解也有这种方法,不过它起名叫模拟,而且是只用了一个哈希表,官方题解有很多时候写的不易看懂,但是思路总体和我们差不多,都是实验当前数字能否减去,直到减不下去了自增下标。

第二种思路,是官方给出的列表法

因为num是有范围的,最大可以达到3999
我们使用四个数组把千百十个位数所有可能的数字对应的罗马字母依次列出来
return只需要返回num各个位数对应在四个数组下标位置的罗马字母的拼接即可

class Solution {
public:

    string intToRoman(int num) {
       string a1[]={"","M","MM","MMM"};
        string a2[]={"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"};
        string a3[]={"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"};
        string a4[]={"","I","II","III","IV","V","VI","VII","VIII","IX"};
        return a1[num/1000]+a2[num%1000/100]+a3[num%100/10]+a4[num%10];
    }
};

没错就是硬列,当时看到这个题解时候还很是震惊


205.同构字符串

205. 同构字符串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/isomorphic-strings/description/?envType=list&envId=sxrVTWKy判断映射,一开始看到这道题很懵,不知道它想表达的是什么含义,后来想明白了,可能是这两天连续刷题有题感了

自写题解
两个哈希表一个存储的是s的字母映射到t,另一个存储的是t字符串各字符是否被当作映射字母而存储在map里
有很多题解选用的是两个map哈希做映射,分别存储s到t和t到s的关系
如果发现此时s和t的位置在两个map里有一个对不上,直接false那个相对思路简单
我这个也是可以的,思路就是map存s到t的映射,set存当前t中字符是否做了映射
然后分别判断一下,是否出现了两个相等字符映射到不等字符,或者两个不相等字符映射到了相等字符的情况
第一次做,想了一段时间才想起用两个哈希表做,之前没做过这种题
不过想清楚了,这道题还是很简单的

这道题和上一道题用两个哈希表做法不一样,上一道题我是不会写官方题解的那种可以使用map第一个值的那个写法,而这道题是真的需要用到两个哈希表,要不然做不出来。

class Solution {
public:
    bool isIsomorphic(string s, string t) {
        unordered_map<char,char>map;
        unordered_set<char>set;
        for(int i=0,j=0;i<s.size()&&j<t.size();++i,++j){
            if(map.find(s[i])==map.end()&&set.find(t[j])==set.end()){
            map.insert({s[i],t[j]});set.insert(t[j]);
            }
            else if(map.find(s[i])!=map.end()&&t[j]!=map[s[i]])
                return false;
            else if(map.find(s[i])==map.end()&&set.find(t[j])!=set.end())
            return false;
        }
        return true;
    }
};

大家这里如果对两个哈希的绑定方法没有看太懂,看下面的题解,也是用到了这一方法,不过我下一个题的笔记做的详细,索性放在下面的题解说细节。


290.单词规律

290. 单词规律 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/word-pattern/description/?envType=list&envId=sxrVTWKy这道也是我觉得很有价值的一道题

自写思路
仍然是使用两个map来记录映射关系
一个存正向映射,另一个存反向映射,双重保障
但是自己又想了想,如果是{a,b}和{b,c}呢?
这a->b b->a记录完映射之后又怎么记录b->c呢
这其实可以理解成,第一个哈希表的作用是起到真正记录映射的作用,而第二个是起到辅助映射而主要的目的是用它来防止重复映射
为什么这么说呢?
我们需要保证的是第一个集合中不等字符不能映射到相同的字符串,而相等字符不能映射到不同字符串
a->b b->a也是相对于此时位置时的双向绑定,但是第二个位置b在第一个哈希是没有映射的,我们之前说了不同字符要映射不同字符串
所以存第一个哈希b->c是没错的
然后是c->b这个c也没在第二个哈希被绑定过
这就能解释得通了
还有一件事,这个解法需要注意的细节有很多
比如说截取字符串时候,我们走到了空格退出,所以是substr(left,j-left)
因为j已经往后走了一个才出来,就不用加1了
再进去循环截取字符串时候,由于是遇到空格退出来,所以我们下次进入不应该从当前位置进入所以要j++,然后更新下一次截取的窗口左值
最后为了避免出现pattern字符串大小小于字符串s的结果
我们选择判断如果此时j没有走到最后返回false
这个样例是合理的,因为pattern应该是随着s来走的,必须是一一对应的
在有字符串的情况下,每一个单词应该严格对应
但是如果没有字符串了,那再多的pattern也没用了
这里测试样例也没有这种的所以结果是什么也不重要了
相信看到这里大家应该有了更深的理解

class Solution {
public:
    bool wordPattern(string pattern, string s) {
        stringstream ss(s);string str;
        unordered_map<char,string>map1;
        unordered_map<string,char>map2;
        for(char c:pattern){
            if(!(ss>>str)||map1.count(c)==1&&map1[c]!=str||map2.count(str)==1&&
            map2[str]!=c)return false;
            map1.insert({c,str});map2.insert({str,c});
        }
        return (ss>>str)?false:true;
    }
};

但是我这个代码并不好,它有一定的缺陷

看官方题解:

也是用双map,我在判断上做了修改并加了注释,如果pattern过多,那么一定会出现i不停向后走直到大于了原本给定str长度的情况
如果是pattern过少,那么出来时,i一定小于str本身的长度
因为根据题解给出的思路来看,无论是否进入下一次循环,左窗口都会走到下一次要进入的位置
这和我们的有所不同,我们的是如果下一次循环无法进入,那么左窗口不会参与移动
而且从官方题解来看
我们的题解有一些缺陷,最致命的错误就是不能判断pattern过多的情况,因为如果是过少我们外边有一个判断,j小于str.size()返回false
但如果过多那一定就是等于了,这一点我们做的并不好,当然leetcode的测试样例也不够完善,让我们的代码通过了

class Solution {
public:
    bool wordPattern(string pattern, string str) {
        unordered_map<string, char> str2ch;
        unordered_map<char, string> ch2str;
        int m = str.length();
        int i = 0;
        for (auto ch : pattern) {
            if (i > m) {//s少于pattern情况
                return false;
            }
            int j = i;
            while (j < m && str[j] != ' ') j++;
            const string &tmp = str.substr(i, j - i);
            if (str2ch.count(tmp) && str2ch[tmp] != ch) {
                return false;
            }
            if (ch2str.count(ch) && ch2str[ch] != tmp) {
                return false;
            }
            str2ch[tmp] = ch;
            ch2str[ch] = tmp;
            i = j + 1;
        }
        return i > m;//最后一次字符串出j正好等于m,而后面有一个i=j+1相当于i=m+1
    }
};

这就是官方题解,循环内部if判断pattern是否太多,最后的判断是判断字符串是否太多

我给官方题解做了一些修改,以便能够更好理解,我把>=修改成了=因为两种情况都是正好等于时候判断之后做出了返回,根本走不到大于

另一种写法来自于网友的神解:

网友大佬的思路十分新奇,让我惊叹
思路: 1.定义两个map,为何定义两个呢? 防止[aaaa]与[cat dog dog cat]、[abba]与[cat cat cat cat]时输出true 2.用stringstream可以自动输出词组 3.判断:!(ss >> s)判断pattern长度是否大于str长度,且将ss容器中字符串赋值给s 4.判断:(map.count(c) == 1 && map[c] != s) || (rmap.count(s) == 1 && rmap[s] != c)) 来判断是否匹配 5.判断:(ss >> s) ? false : true 判断str长度是否大于pattern长度
 

class Solution {
public:
    bool wordPattern(string pattern, string s) {
        stringstream ss(s);string str;
        unordered_map<char,string>map1;
        unordered_map<string,char>map2;
        for(char c:pattern){
            if(!(ss>>str)||map1.count(c)==1&&map1[c]!=str||map2.count(str)==1&&
            map2[str]!=c)return false;
            map1.insert({c,str});map2.insert({str,c});
        }
        return (ss>>str)?false:true;
    }
};

这个思路是我没有设想过的,但是三个题解都是0ms的时间!


217.存在重复元素

217. 存在重复元素 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/contains-duplicate/description/?envType=list&envId=sxrVTWKy这是一道简单题,我给出两种思路,包括我对哈希表的一个总结。

一眼就看出来的两种思路
一种是用哈希表存储遍历过的值,一边存储一边判断,当前数是否出现过在哈希表中,如果出现就返回真,这个时间复杂度快
另一种是排序然后遍历,当前数和上一个数是否相等,这样时间稍慢一些
这道题很自然的想到用set去做,这里也顺便说一下,我对于哈希表的理解
什么时候用数组?我认为数组做哈希,用的不多,一般在给定数据有规律可循并且要存的数据有限少,比如说需要存储一个给定字符串各个字符出现的频率,这样的用数组
set和map很常用,set存储不那么有规律的数据,就比如说这道题,只要没出现过的数字我们都存上,这里你用vector,很难去判断当前数字是否在数组里出现过,因为数字是无序的,没有规律的出现,而且数组没有能够快速查找无规律数字是否出现过的功能,而set就很适用这种场景
但是如果用定长数组说不定可行,存储时候就是arr【nums【i】】=nums【i】
但是这又有新的问题,一个一开始你不知道给定数据中最大数字是多少,也就是说你不能马上确定开多大数组,第二点是开过大数据,但是用到的很少,会浪费大量空间
再说说map,map是用来存映射关系,比如说,一个字母映射另一个字母,或者需要记录某数字出现的频次,这种时候选用map是最合适的

这里只给出哈希表做法,因为毕竟这是哈希表专题,而且遍历方法也好写,这里就不写了

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int>set;
        for(int i=0;i<nums.size();++i){
            if(set.find(nums[i])!=set.end())return true;
            else set.insert(nums[i]);
        }
        return false;
    }
};

以上是本期哈希表的全部内容,对你有用的话别忘了一键三连哦,如果是互粉回访我也会做的!

大家有什么想看的题解,或者想看的算法专栏、数据结构专栏,可以去看看往期的文章,有想看的新题目或者专栏也可以评论区写出来,讨论一番,本账号将持续更新,期待您的关注

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

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

相关文章

代码随想录Day02 数组基础2 leetcode T977有序数组的平方, T209 长度最小的子数组,T59 螺旋矩阵II

本文思路和详细解答来源于: 代码随想录 视频讲解见: 双指针法经典题目 | LeetCode&#xff1a;977.有序数组的平方_哔哩哔哩_bilibili Leetcode T977 有序数组的平方 题目链接: 977. 有序数组的平方 - 力扣&#xff08;LeetCode&#xff09; 思路1: 暴力求解 这里先解释一下非…

如何满足计算机化系统验证(CSV):制药企业的指南

随着科技的不断发展&#xff0c;制药企业在其日常运营中越来越多地依赖计算机化系统。这些系统涵盖了从研发到生产再到分销的各个领域&#xff0c;它们对于确保药品质量、跟踪生产流程以及维护患者安全至关重要。为了满足监管机构的法规要求&#xff0c;如美国FDA、欧盟Annex 1…

别再费劲配音了!小说推文视频一键生成,并带全自动配音

下面教你轻松一键制作出精彩的小说推文视频。 1. 输入文案生成小说推文视频 小说推文视频可以根据你输入的文案自动生成精美的视频内容&#xff0c;无需手动操作。只需提供文案&#xff0c;小说推文视频就能为你制作出令人惊艳的视频作品。 2. 自动小说推文配音 不用再费心去…

力扣:108. 将有序数组转换为二叉搜索树(Python3)

题目&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 来源&#xff1a;力扣&#xff08;LeetCode&…

C#的属性讲解

文章目录 属性自动实现属性访问器内写逻辑属性不存储值其他文章 属性 在C#中&#xff0c;属性是一种特殊的成员&#xff0c;用于封装类的字段。它们提供了一种简洁和安全的方式来访问和设置类的状态和行为。 属性由两个访问器组成&#xff1a;get&#xff08;获取器&#xff…

测试工程师需要具备哪些“技能”?

1、良好的沟通 相信大家都在网上看到过各种吐槽程序员不解风情的段子&#xff0c;开怀大笑之余深思&#xff0c;作为一个测试工程师又何尝不是如此&#xff1f;通常沟通技能成为横亘在测试工程师与其他合作部门之间的万丈鸿沟&#xff0c;也成为测试工程师成长的最大瓶颈。下面…

【软件设计师-从小白到大牛】上午题基础篇:第三章 数据库系统

文章目录 前言章节提要一、三级模式两级映射真题链接 二、数据库的设计过程真题链接 三、E-R模型真题链接 四、关系代数SQL基础&#xff08;补充&#xff09; 五、规范化理论1、函数依赖2、价值与用途3、键4、范式5、模式分解 六、并发控制真题链接分布式数据库特点&#xff08…

二值贝叶斯滤波计算4d毫米波聚类目标动静属性

机器人学中有些问题是二值问题&#xff0c;对于这种二值问题的概率评估问题可以用二值贝叶斯滤波器binary Bayes filter来解决的。比如机器人前方有一个门&#xff0c;机器人想判断这个门是开是关。这个二值状态是固定的&#xff0c;并不会随着测量数据变量的改变而改变。就像门…

关于计算机找不到d3dx9_43.dll,无法继续执行代码修复方法

d3dx9_43.dll是一个动态链接库文件&#xff0c;它是DirectX的一个组件&#xff0c;主要用于处理游戏中的图形、声音等多媒体元素。当这个文件丢失时&#xff0c;可能会导致以下问题&#xff1a; 1. 游戏无法正常运行&#xff1a;由于d3dx9_43.dll负责处理游戏中的多媒体元素&a…

Qt地铁智慧换乘系统浅学( 一 )存储站点,线路信息

存储 定义所需要的容器定义最大最小经纬度[统计站点信息 在经纬度网站](https://map.jiqrxx.com/jingweidu/)读取统计的信息存储到容器其他的一些相关函数debug 显示存储的信息更新最小最大经纬度的函数获取两点之间的距离 根据经纬度 定义所需要的容器 extern QMap<QStrin…

寻找单身狗

在一个数组中仅出现一次&#xff0c;其他数均出现两次&#xff0c;这个出现一次的数就被称为“单身狗“。 一.一个单身狗 我们知道异或运算操作符 ^ &#xff0c;它的特点是对应二进制位相同为 0&#xff0c;相异为 1。 由此我们容易知道两个相同的数,进行异或运算得到的结果…

tkinter的Canvas组件,绘画基本知识

一、说明 画布组件是Tkinter画图的最重要组件。画布对象是几何绘制、动画绘制的不二选项&#xff0c;本文专门对画布Canvas进行详细描述&#xff0c;并配以适当代码支持。 二、tkinter的Canvas组件&#xff0c;绘画基本知识 Canvas组件&#xff0c;可以用来绘图&#xff0c;也…

【Java 基础篇】Java反射详解:深入了解Java的镜像世界

Java是一门面向对象的编程语言&#xff0c;其强大之处之一就是能够在运行时检查、获取和操作类、方法、字段等程序元素。这一特性就是通过Java的反射机制实现的。本文将深入介绍Java反射&#xff0c;包括它的基本概念、使用方法、常见应用场景和注意事项。无需担心&#xff0c;…

云计算战略:选择适合你业务的云平台

文章目录 云计算的概述选择云平台的关键因素1. 业务需求2. 预算3. 性能要求4. 数据隐私和合规性 示例&#xff1a;选择适合的云平台业务需求预算性能要求数据隐私和合规性 代码示例&#xff1a;使用云平台服务结论 &#x1f389;欢迎来到云计算技术应用专栏~云计算战略&#xf…

“源启2.0”:从自上而下的解构,到自下而上的重构

从垂直打穿、到应用重构&#xff0c;中电金信赋能行业数字化转型之路既“向下走”、也“向上看”。“向上”先理解和吃透客户的企业战略&#xff0c;进而自上而下地将企业战略拆解为业务架构&#xff0c;“向下”再将业务架构拆解为应用架构和数据架构&#xff0c;并进一步对齐…

JS预解析/编译(变量提升):var(仅声明,无赋值)、function变量 创建作用域

目录 变量提升/预定义 function和var 重名&#xff1a;函数覆盖变量 不执行代码 重复声明&#xff1a;只提升一次 函数形参&#xff1a;变量提升 带 var 和不带 var 全局作用域&#xff1a; window 的属性 私有/函数作用域&#xff1a; 带 var 的是私有变量 IIFE 函…

Linux 终端与进程

有趣的问题 Linux 中的 终端&#xff0c;控制台&#xff0c;TTY&#xff0c;PTY 究竟是什么&#xff1f;它们与进程有什么关系&#xff1f; 历史回顾&#xff1a;控制台 (Console) 控制台是一个直接控制设备的面板 (属于设备的一部分) 计算机设备的控制台&#xff1a;按键 &…

2FSK调制解调VHDL,Quartus

名称&#xff1a;2FSK调制解调VHDL&#xff08;代码在文末付费下载&#xff09; 软件&#xff1a;Quartus 语言&#xff1a;VHDL 要求&#xff1a;使用VHDL实现2FSK的调制和解调&#xff0c;并进行仿真 代码下载&#xff1a;2FSK调制解调VHDL,Quartus_Verilog/VHDL资源下载…

C++ stack queue模拟实现

目录 一.stack 二.queue 三.deque STL中有6大组件&#xff0c;我们前面讲的string/vector/list是容器&#xff0c;还有迭代器&#xff0c;以及算法&#xff08;比如find&#xff0c;swap&#xff0c;reverse&#xff0c;sort&#xff0c;merge函数&#xff09;&#xff0c;仿函…

【Java 基础篇】Java Stream 流详解

Java Stream&#xff08;流&#xff09;是Java 8引入的一个强大的新特性&#xff0c;用于处理集合数据。它提供了一种更简洁、更灵活的方式来操作数据&#xff0c;可以大大提高代码的可读性和可维护性。本文将详细介绍Java Stream流的概念、用法和一些常见操作。 什么是Stream…