← 返回题目列表

如何把罗马数字转换成整数?(LeetCode 13)

高频 简单 第 2 / 27 题 更新于 2026/07/28
数学与数论罗马数字模拟哈希映射

简化版

把罗马数字字符串转成整数。罗马数字用 I=1, V=5, X=10, L=50, C=100, D=500, M=1000 表示,通常从大到小相加;但有六种「小在大前」的减法情形(IV=4、IX=9、XL=40、XC=90、CD=400、CM=900)。核心规则:从左到右扫描,如果当前字符代表的值 < 它右边字符的值,就减去它,否则加上它。因为「小的在大的左边」意味着这是减法组合。

详细版

int romanToInt(String s) {
    Map<Character, Integer> val = Map.of(
        'I', 1, 'V', 5, 'X', 10, 'L', 50,
        'C', 100, 'D', 500, 'M', 1000);
    int total = 0;
    for (int i = 0; i < s.length(); i++) {
        int cur = val.get(s.charAt(i));
        // 若当前值 < 右边值,说明是减法组合(如 IV),减去当前值
        if (i + 1 < s.length() && cur < val.get(s.charAt(i + 1))) {
            total -= cur;
        } else {
            total += cur;
        }
    }
    return total;
}
  • 映射表:7 个罗马字符到数值。
  • 判断加还是减:当前字符值 < 右邻字符值 → 减(减法组合的前一位),否则加。
  • 减法组合:IV、IX、XL、XC、CD、CM 共 6 种,都是「小值在大值左边」。
  • 复杂度:O(n) 时间、O(1) 空间。

完整版教学

一、罗马数字规则速览

罗马数字用 7 个字母表示数值:

字符IVXLCDM
1510501005001000

基本规则:一般从大到小排列、数值相加(如 MCMXCIV… 先不管)。VIII = 5+1+1+1 = 8XXVII = 10+10+5+1+1 = 27

特殊规则(减法):为避免连写四个相同字符,规定 6 种「小值放在大值左边表示减」的组合:

  • IV = 4(5-1)、IX = 9(10-1)
  • XL = 40(50-10)、XC = 90(100-10)
  • CD = 400(500-100)、CM = 900(1000-100)

二、核心洞察:小在大前则减

观察减法组合的共性:当一个较小的数值出现在较大数值的左边时,它要被减掉。而正常情况(大在前、小在后)是相加。所以有个统一的判断规则:

从左到右扫描每个字符,若它的值 < 右边相邻字符的值,就减去它;否则加上它。

  • IV:I(1) < V(5) → 减 I,即 -1 + 5 = 4
  • VI:V(5) > I(1) → 加 V,5 + ... ,I 是最后一位直接加,5 + 1 = 6

这个规则一举涵盖了所有加法和 6 种减法情形,不用单独枚举减法对。

三、逐位处理逻辑

遍历字符串每个字符 s[i]

  • 取它的值 cur
  • 看右邻:若 i+1 存在且 cur < val[s[i+1]](当前比右边小)→ total -= cur(它是减法组合的前一位)。
  • 否则 → total += cur

最后一位没有右邻,一定是加。累加完即结果。

MCMXCIV(1994):

  • M(1000) ≥ C → +1000
  • C(100) < M(1000) → -100
  • M(1000) ≥ X → +1000
  • X(10) < C(100) → -10
  • C(100) ≥ I → +100
  • I(1) < V(5) → -1
  • V(5) 最后一位 → +5
  • 合计 1000-100+1000-10+100-1+5 = 1994。✔

四、另一种等价写法:先全加、遇减法对减两倍

也可以「先把所有字符值全加起来,再对每个减法组合减去 2 倍的小值」。但「比较相邻、小则减」的写法更简洁通用,推荐。用 HashMapswitch 存映射都行,字符少,O(1) 空间。

五、反向问题:整数转罗马数字(12)

孪生题 整数转罗马数字(12)贪心:准备一个从大到小的「数值-符号」对照表(含 1000、900、500、400、100、90、50、40、10、9、5、4、1 及对应符号),从最大的开始,能减就减、拼上对应符号,直到把整数减到 0。两题一起掌握,一个是「罗马→数字」的模拟、一个是「数字→罗马」的贪心。

六、相邻比较为何能统一加减规则

合法罗马数字中,某个较小符号放在更大符号之前时,它就是减法贡献;否则为加法贡献。逐字符看右邻相当于把 IV 计算为 -I+V,把普通降序段逐项相加,不需要先把字符配成 token。

MCMXCIV
M=+1000
C<M -> -100,M=+1000
X<C -> -10,C=+100
I<V -> -1,V=+5
总和 1000-100+1000-10+100-1+5
结果 1994
校验维度本题必须保持的结论
循环/递推不变量扫描到 i 后,total 是已决定符号的前缀贡献;只有当前值与右邻关系决定当前正负。
边界条件题目通常保证输入是合法罗马数字;若要求校验合法性,仅靠“小于右邻则减”不够。
复杂度与代价单次线性扫描 O(n),映射表固定 7 项为 O(1) 空间。
自检方式用最小输入、典型输入和触及边界的输入分别手推,结果应与定义一致。

这组推演的作用不是替代证明,而是把抽象规则落到每一步状态变化上。面试写完代码后,应主动指出不变量如何保证不漏解,以及边界为何不会越界或死循环。若手推结果与定义不一致,应先修正状态语义,而不是继续给代码打补丁。

七、面试现场如何验证这道题

数学题不能只凭样例相信公式,必须同时核对定义域、推导条件和定宽整数边界。本题应先复述这条不变量:扫描到 i 后,total 是已决定符号的前缀贡献;只有当前值与右邻关系决定当前正负。只有代码的初始化、更新顺序和终止条件都维持它,样例通过才具有证明力。

  • 典型路径从“MCMXCIV”开始手推,最后应得到“结果 1994”。
  • 边界复核:题目通常保证输入是合法罗马数字;若要求校验合法性,仅靠“小于右邻则减”不够。
  • 代价复核:单次线性扫描 O(n),映射表固定 7 项为 O(1) 空间。
  • 用 0、1、最小合法值和最大合法值检查公式的定义域。
  • 乘法、取绝对值或取负前先判断是否可能触及 Integer.MIN_VALUE 等不对称边界。
  • 若算法依赖单调性、抵消或整除关系,要明确题设保证何时成立、何时必须额外验证。

最后要主动构造一个“少写一次循环、改变一个不等号或交换两行更新就会失败”的反例。能说清反例破坏了哪条不变量,比只报出复杂度更能证明真正掌握了算法。

记忆钩子:本题的代码可以压缩,但“扫描到 i 后,total 是已决定符号的前缀贡献;只有当前值与右邻关系决定当前正负。”这条正确性主线不能省。

八、常见误区与追问

  • 误区:任意小字符放在大字符前都构成合法减法。 合法表示只允许 IV、IX、XL、XC、CD、CM 等规定组合;本题输入合法才可简化判断。
  • 误区:遇到减法组合要同时跳过两个字符。 逐字符正负贡献法无需跳过,前者减、后者正常加即可。
  • 误区:最后一个字符也要与右邻比较。 它没有右邻,贡献必为正,直接相加。
  • 追问:III 如何计算? 每个 I 都不小于右邻或位于末尾,因此 1+1+1=3
  • 追问:整数转罗马为何使用降序表? 每次选择不超过剩余值的最大合法符号,包含减法组合后可贪心构造规范表示。
  • 追问:若输入可能是 IL 怎么办? 数值扫描会算出 49,但它不是规范写法;需要额外语法规则或回转后比对来验证。

九、加强记忆

罗马数字转整数 = 从左扫描,「小在大前则减,否则加」。7 个字符映射到值(I/V/X/L/C/D/M = 1/5/10/50/100/500/1000)。规则:若当前字符值 < 右邻字符值 → total -= cur(减法组合前一位),否则 total += cur。这一条统一涵盖 6 种减法组合(IV/IX/XL/XC/CD/CM)和所有加法。O(n) O(1)。反向题「整数转罗马」用贪心(大数值符号优先能减就拼)。核心:小的在大的左边,就减掉它