
本文详解 leetcode 第 12 题“整数转罗马数字”的常见逻辑错误与高效解法,重点剖析原代码中无限循环的根本原因,并提供基于贪心策略、时间复杂度 o(1) 的标准实现。
本文详解 leetcode 第 12 题“整数转罗马数字”的常见逻辑错误与高效解法,重点剖析原代码中无限循环的根本原因,并提供基于贪心策略、时间复杂度 o(1) 的标准实现。
原代码的核心问题在于 findNextDigit 方法的设计存在严重逻辑缺陷:它试图通过动态调整 tmp(幂次)和 k(倍数)来逼近目标数值,但控制条件 Math.pow(10, tmp) * k 与分支判断 <code>num - Math.pow(10, tmp) 缺乏数学一致性,导致循环无法收敛——例如当 <code>num = 9 时,tmp 在 0 和 1 间震荡,k 持续递增却未被约束,最终陷入死循环。
更本质的问题是:罗马数字的表示具有确定性与有限性。所有合法组合(如 4→"IV"、9→"IX"、40→"XL" 等)共 13 种,且必须按从大到小顺序贪心匹配。因此,无需动态计算位权或倍数,而应直接枚举预定义的数值-符号映射对。
以下是推荐的工业级实现(已通过 LeetCode 全部用例):
public class Solution {
public String intToRoman(int num) {
StringBuilder ans = new StringBuilder();
// 按降序排列所有关键数值(含减法规则:4,9,40,90...)
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"};
for (int i = 0; i < values.length; i++) {
// 贪心匹配:只要当前数值可减,就重复添加对应符号
while (num >= values[i]) {
ans.append(symbols[i]);
num -= values[i];
}
}
return ans.toString();
}
}✅ 关键优化点说明:
- 贪心策略保障正确性:因数组严格降序,每次取最大可能值,自然满足罗马数字“大数在前、减法前置”的规则;
-
StringBuilder替代String:避免ans += ...导致的多次字符串拷贝(Java 中String不可变),显著提升性能; -
时间复杂度为 O(1):循环最多执行 13 × 3 = 39 次(如
num=3999时 M×3 + CM×1 + XC×1 + IX×1),与输入规模无关; -
无浮点运算:彻底规避
Math.pow()带来的精度误差与性能开销。
⚠️ 注意事项:
- 切勿在循环中修改用于控制条件的变量(如原代码中
tmp和k的耦合更新); - 映射数组必须严格按降序排列,否则会破坏贪心逻辑(例如若
"IV"(4)排在"V"(5)之前,则 6 会被错误拆分为"IV"+"I"而非"VI"); - 输入范围为
1–3999,无需额外校验,但生产环境建议添加if (num 3999) throw new IllegalArgumentException();。
该解法简洁、健壮、高效,是解决此类“有限规则映射”问题的标准范式。

















