一、题目描述
罗马数字包含以下七种字符: I
,V
,X
,L
,C
,D
和 M
。
字符 | 数值 |
---|---|
I | 1 |
V | 5 |
X | 10 |
L | 50 |
C | 100 |
D | 500 |
M | 1000 |
例如, 罗马数字 2
写做 II
,即为两个并列的 1
。12
写做 XII
,即为 X + II
。 27
写做 XXVII
, 即为 XX + V + II
。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4
不写做 IIII
,而是 IV
。数字 1
在数字 5
的左边,所表示的数等于大数 5
减小数 1
得到的数值 4
。同样地,数字 9
表示为 IX
。这个特殊的规则只适用于以下六种情况:
I
可以放在V
(5) 和X
(10) 的左边,来表示 4 和 9。X
可以放在L
(50) 和C
(100) 的左边,来表示 40 和 90。C
可以放在D
(500) 和M
(1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
示例
- 示例 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
andIV = 4
.
- 输入:
二、解题思路与多方法实现
方法一:常规遍历法
思路
- 首先,我们需要创建一个映射,将罗马字符和对应的整数值关联起来。
- 然后,从左到右遍历罗马数字字符串。对于每个字符,我们获取其对应的整数值。
- 接着,判断当前字符对应的数值是否小于下一个字符对应的数值。如果是,说明遇到了特殊情况(如
IV
、IX
等),此时需要用下一个字符的数值减去当前字符的数值,并将索引向后移动两位;否则,直接将当前字符的数值累加到结果中,并将索引向后移动一位。
代码实现(Java)
import java.util.HashMap;
import java.util.Map;
public class RomanToInteger {
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 result = 0;
int i = 0;
while (i < s.length()) {
int currentValue = romanMap.get(s.charAt(i));
if (i + 1 < s.length()) {
int nextValue = romanMap.get(s.charAt(i + 1));
if (currentValue < nextValue) {
// 特殊情况,如 IV、IX 等
result += nextValue - currentValue;
i += 2;
} else {
result += currentValue;
i++;
}
} else {
// 最后一个字符
result += currentValue;
i++;
}
}
return result;
}
}
代码解释
- 创建映射:使用
HashMap
存储罗马字符和对应的整数值,方便后续查找。 - 遍历字符串:使用
while
循环遍历罗马数字字符串。对于每个字符,获取其对应的整数值currentValue
。 - 特殊情况判断:检查是否存在下一个字符,如果存在,获取下一个字符的数值
nextValue
。若currentValue < nextValue
,说明是特殊情况,将nextValue - currentValue
累加到结果中,并将索引i
向后移动两位;否则,直接将currentValue
累加到结果中,并将索引i
向后移动一位。 - 处理最后一个字符:当遍历到最后一个字符时,直接将其对应的数值累加到结果中。
方法二:逆序遍历法
思路
- 同样先创建罗马字符和整数值的映射。
- 从右到左逆序遍历罗马数字字符串。对于每个字符,获取其对应的整数值。
- 比较当前字符对应的数值和前一个处理过的字符对应的数值。如果当前字符的数值小于前一个处理过的字符的数值,说明是特殊情况,需要从结果中减去当前字符的数值;否则,将当前字符的数值累加到结果中。
代码实现(Java)
import java.util.HashMap;
import java.util.Map;
public class RomanToIntegerReverse {
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 result = 0;
int prevValue = 0;
for (int i = s.length() - 1; i >= 0; i--) {
int currentValue = romanMap.get(s.charAt(i));
if (currentValue < prevValue) {
result -= currentValue;
} else {
result += currentValue;
}
prevValue = currentValue;
}
return result;
}
}
代码解释
- 创建映射:和方法一一样,使用
HashMap
存储罗马字符和对应的整数值。 - 逆序遍历字符串:使用
for
循环从右到左遍历罗马数字字符串。对于每个字符,获取其对应的整数值currentValue
。 - 特殊情况判断:比较
currentValue
和prevValue
(前一个处理过的字符的数值)。如果currentValue < prevValue
,说明是特殊情况,从结果中减去currentValue
;否则,将currentValue
累加到结果中。 - 更新前一个值:每次处理完一个字符后,将
prevValue
更新为currentValue
。
三、复杂度分析
时间复杂度
- 常规遍历法:需要遍历一次罗马数字字符串,时间复杂度为 O ( n ) O(n) O(n),其中 n n n 是字符串的长度。
- 逆序遍历法:同样需要遍历一次字符串,时间复杂度也是 O ( n ) O(n) O(n)。
空间复杂度
- 常规遍历法:使用了一个
HashMap
来存储罗马字符和整数值的映射,空间复杂度为 O ( 1 ) O(1) O(1),因为罗马字符的种类是固定的(只有 7 种)。 - 逆序遍历法:也使用了一个
HashMap
存储映射,空间复杂度同样为 O ( 1 ) O(1) O(1)。
四、总结
“罗马数字转整数”这道简单题虽然规则上有特殊情况,但通过合理的算法设计可以轻松解决。常规遍历法和逆序遍历法都能有效地将罗马数字转换为整数,它们的时间复杂度和空间复杂度基本相同。常规遍历法是正向处理字符串,需要对特殊情况进行额外的判断和索引移动;逆序遍历法则是逆向处理,通过比较相邻字符的数值来处理特殊情况,代码相对简洁。在实际应用中,可以根据个人的编程习惯选择合适的方法。希望通过这篇博客,大家能对罗马数字的转换有更深入的理解,同时掌握不同的解题思路和技巧。