13、罗马数字转整数




13、罗马数字转整数
鲁迅说过,三天不刷 LeetCode,手里发痒,今天继续来刷《二哥的 LeetCode 刷
题笔记》吧。
题意
给你一个罗马数字,要求你给出它相应的整数。
至于罗马数字是什么,第 12 题已经解释过了,这里就不再赘述。
难度
简单
示例
示例 1:
输入: s = "III"
输出: 3
示例 2:
输入: s = "IV"
输出: 4
示例 3:
输入: s = "IX"
输出: 9
示例 4:
输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
分析 1
这道题目和第 12 题是相反的,第 12 题是整数转罗马数字,这道题目是罗马数字转整
数。
我们可以先把罗马数字和整数的对应关系列出来:
我们可以发现,罗马数字和整数的对应关系是一一对应的,所以我们可以把这些对应关系
放到一个 Map 中,然后遍历罗马数字,依次取出对应的整数,然后相加即可。
需要注意的是,如果一个较小的数字在较大的数字前面,比如 IV、IX、XL、XC、CD、
CM,需要减去这个较小的数字(因为它表示的是一个减法操作,如 IV 表示 4)。
来看题解:
class Solution {
public int romanToInt(String s) {
// 创建一个哈希表存储罗马数字和对应的整数值
Map<Character, Integer> romanMap = new HashMap<>();
romanMap.put('I', 1);
romanMap.put('V', 5);
romanMap.put('X', 10);
romanMap.put('L', 50);
romanMap.put('C', 100);
romanMap.put('D', 500);
romanMap.put('M', 1000);
int sum = 0;
int prevValue = 0;
// 从后向前遍历罗马数字字符串
for (int i = s.length() - 1; i >= 0; i--) {
int currValue = romanMap.get(s.charAt(i));
// 如果当前值小于之前的值,则减去当前值;否则,加上当前值
if (currValue < prevValue) {
sum -= currValue;
} else {
sum += currValue;
}
prevValue = currValue;
}
return sum;
}
}
我们首先创建一个哈希表来存储罗马数字字符及其对应的整数值。
然后,从字符串的最后一个字符开始向前遍历(这样可以更方便地处理减法情况)。
对于每个字符,我们查找其对应的整数值。
如果当前字符代表的数值小于其右侧的数值,这意味着我们遇到了一个减法情况(如 IV
或 IX),因此我们从总和中减去这个数值。
如果当前字符代表的数值大于等于其右侧的数值,我们就简单地加上这个数值。
最终累加的结果就是罗马数字字符串所表示的整数值。
来看一下题解效率,还不错:
013.%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97%E8%BD%AC%E6%95%B4%E
6%95%B0-20240122105021.png
分析 2
我们可以把上面的代码再优化一下,把 Map 换成数组,这样效率会更高一点。
class Solution {
public int romanToInt(String s) {
int[] values = new int[128]; // ASCII 码的范围足够覆盖所有字符
values['I'] = 1;
values['V'] = 5;
values['X'] = 10;
values['L'] = 50;
values['C'] = 100;
values['D'] = 500;
values['M'] = 1000;
int sum = 0;
int prevValue = 0;
for (int i = s.length() - 1; i >= 0; i--) {
int currValue = values[s.charAt(i)];
if (currValue < prevValue) {
sum -= currValue;
} else {
sum += currValue;
}
prevValue = currValue;
}
return sum;
}
}
除了把 Map 换成数组,其他的代码都是一样的。所以我就没再加注释,来看一下效率提
升:
013.%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97%E8%BD%AC%E6%95%B4%E
6%95%B0-20240122105301.png
OK,这次 beat 100% 了,效率提升了一大截。
这里需要注意的是,数组的长度为 128,这是基于 ASCII 码的范围设计的。ASCII 码是一
种字符编码标准,它将英文字符、数字、标点符号等映射到 0-127 的整数上。
当使用字符 int currValue = values[s.charAt(i)] 作为数组索引时,字符实际上被转换为
其对应的 ASCII 值。例如,字符 ‘A’ 对应 ASCII 值 65。
使用 128 大小的数组可以确保直接通过字符的 ASCII 值作为索引来访问数组元素,这样的
访问速度更快。
虽然题目中只用到了 7 个罗马字符,但这样可以确保代码的通用性。这是另外一个非常巧
妙的地方。
总结
这道题的巧妙之处就在于:对于罗马数字来说,把一个小值放在大值的左边,就是做减
法,否则为加法。
013.%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97%E8%BD%AC%E6%95%B4%E
6%95%B0-20240122104623.png
剩下的处理就非常简单了。由于罗马数字的数量非常有限,总共就 7 个,仅含字符 (‘I’,
‘V’, ‘X’, ‘L’, ‘C’, ‘D’, ‘M’),我们也可以通过数组来替代 HashMap,这样的效率会更快一点,
虽然 HashMap 的底层也是数组。但毕竟 HashMap 是对数组的一种封装,加上了链表和红
黑树,包括扩容等操作,所以效率肯定没有直接使用数组来得高。
这道题目所涉及到的 Java 基础知识有:
• HashMap
• 数组
• char 型字符
• for 循环
力扣链接:https://leetcode.cn/problems/roman-to-integer/