LeetCode 12 整数转罗马数字:一题讲透贪心思想

11 阅读3分钟

「整数转罗马数字」是一道非常有代表性的规则型算法题
它不考复杂数据结构,也不考高深数学,但却特别适合用来考察:

  • 规则抽象能力
  • 贪心思想的理解
  • 代码设计是否清晰、优雅

这篇笔记会从“为什么这么做”开始,一步步讲清楚。


一、题目回顾

给定一个整数 num(范围是 1 <= num <= 3999),
要求将它转换成对应的罗马数字字符串


二、罗马数字的规则整理(关键)

在动手写代码之前,必须先把规则彻底整理清楚

1. 基本符号

I = 1
V = 5
X = 10
L = 50
C = 100
D = 500
M = 1000

2. 特殊规则(减法表示)

以下组合不能拆开写:

IV = 4
IX = 9
XL = 40
XC = 90
CD = 400
CM = 900

这一步非常重要,否则后面逻辑会变复杂。


三、核心解题思路:贪心 + 映射表

这道题最本质的思路只有一句话:

每一步,优先使用不超过 num 的最大罗马数字

这就是典型的贪心策略

为什么贪心是对的?

因为:

  • 罗马数字是从大到小组合的
  • 大数字优先使用,能保证位数最少、形式唯一
  • 规则是固定的,不存在“回头更优”的情况

四、把规则变成程序能用的形式

我们直接把所有可能用到的罗马数字,按从大到小顺序列出来

int[] values = {
    1000, 900, 500, 400,
    100, 90, 50, 40,
    10, 9, 5, 4,
    1
};

String[] symbols = {
    "M", "CM", "D", "CD",
    "C", "XC", "L", "XL",
    "X", "IX", "V", "IV",
    "I"
};

这里有两个细节:

  • 特殊规则(900、400、90 等)必须提前放进去
  • 顺序必须是从大到小

五、完整 Java 实现

class Solution {
    public String intToRoman(int num) {
        int[] values = {
            1000, 900, 500, 400,
            100, 90, 50, 40,
            10, 9, 5, 4,
            1
        };

        String[] symbols = {
            "M", "CM", "D", "CD",
            "C", "XC", "L", "XL",
            "X", "IX", "V", "IV",
            "I"
        };

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < values.length; i++) {
            while (num >= values[i]) {
                num -= values[i];
                sb.append(symbols[i]);
            }
        }
        return sb.toString();
    }
}

六、用一个完整例子走一遍流程

num = 1994 为例:

1994 >= 1000  M     剩余 994
994  >= 900   CM    剩余 94
94   >= 90    XC    剩余 4
4    >= 4     IV    剩余 0

最终结果:

"MCMXCIV"

这个过程非常符合人类对罗马数字的理解方式。


七、为什么一定要用 StringBuilder?

在 Java 中:

  • String 是不可变对象
  • 每次拼接都会创建新对象,效率低

StringBuilder

  • 可变
  • 拼接是原地操作
  • 是这类题的标准选择

这是面试中默认的最佳实践


八、复杂度分析

时间复杂度:

O(1)

原因是罗马数字规则是固定的,最多循环 13 次。

空间复杂度:

O(1)

只使用了常量级数组和变量。


九、这道题真正考察的能力

这道题表面看是字符串题,实际上考察的是:

  • 能不能把复杂规则抽象成结构化数据
  • 是否具备用“贪心思想”解决问题的意识
  • 代码是否清晰、易维护、无多余分支