摘要
本文详细解析了 LeetCode 第 12 题「整数转罗马数字」的两种主流解法:
核心解法:
- 硬编码枚举法:将整数按千、百、十、个位分解,每位数字对应预定义的罗马数字组合,最后拼接结果。时间复杂度 O(1),代码直观易懂,适合面试场景。
- 贪心算法:预先定义所有罗马数字符号及其对应数值,从大到小遍历并尽可能使用当前最大符号。同样 O(1) 复杂度,代码稍长但更通用。
关键要点:
- 罗马数字规则:7 种基本字符 + 6 种特殊减法组合(IV、IX、XL、XC、CD、CM)
- 输入范围限制:1-3999(罗马数字无 0 表示,最大为 MMMCMXCIX)
- 两种方法空间复杂度均为 O(1),性能接近,硬编码法略优
适用场景:硬编码法适合初学者和面试;贪心算法更易扩展规则变化。
文章包含完整的 Java 代码实现、复杂度分析、测试用例及常见问题解答,帮助读者全面掌握该题解法。
题目描述
罗马数字包含以下七种字符:I,V,X,L,C,D和M。
字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000罗马数字的规则如下:
- 通常情况:罗马数字按照从左到右、从大到小的顺序书写,表示这些数字相加的和。
- 减法规则:有六种特殊情况使用减法表示:
IV= 4 (5-1)IX= 9 (10-1)XL= 40 (50-10)XC= 90 (100-10)CD= 400 (500-100)CM= 900 (1000-100)
题目要求:给定一个整数num(1 ≤ num ≤ 3999),将其转换为罗马数字。
解题思路分析
方法一:硬编码枚举法(推荐)
这是最简单直观的方法。由于罗马数字的表示规则相对固定,且题目限制了输入范围(1-3999),我们可以将每一位数字对应的罗马数字表示预先定义好。
核心思路:
- 将整数按千位、百位、十位、个位分解
- 每位数字对应一组固定的罗马数字组合
- 将四部分拼接起来
优点:
- 时间复杂度 O(1),只需要常数次操作
- 代码清晰易懂
- 执行效率高
方法二:贪心算法
另一种常见解法是使用贪心策略:
- 预先定义所有可能的罗马数字符号及其对应的数值
- 从大到小遍历这些符号
- 每次尽可能使用当前最大的符号
这种方法同样高效,但代码稍长一些。
代码实现
方法一:硬编码枚举法
classSolution{publicStringintToRoman(intnum){// 分解数字的每一位inta=num%10;// 个位num/=10;intb=num%10;// 十位num/=10;intc=num%10;// 百位num/=10;intd=num%10;// 千位// 拼接结果returnnum4(d)+num3(c)+num2(b)+num1(a);}// 处理千位 (1000-3000)privateStringnum4(intn){switch(n){case1:return"M";case2:return"MM";case3:return"MMM";default:return"";}}// 处理百位 (100-900)privateStringnum3(intn){switch(n){case1:return"C";case2:return"CC";case3:return"CCC";case4:return"CD";case5:return"D";case6:return"DC";case7:return"DCC";case8:return"DCCC";case9:return"CM";default:return"";}}// 处理十位 (10-90)privateStringnum2(intn){switch(n){case1:return"X";case2:return"XX";case3:return"XXX";case4:return"XL";case5:return"L";case6:return"LX";case7:return"LXX";case8:return"LXXX";case9:return"XC";default:return"";}}// 处理个位 (1-9)privateStringnum1(intn){switch(n){case1:return"I";case2:return"II";case3:return"III";case4:return"IV";case5:return"V";case6:return"VI";case7:return"VII";case8:return"VIII";case9:return"IX";default:return"";}}}方法二:贪心算法(备选)
classSolution{publicStringintToRoman(intnum){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"};StringBuilderroman=newStringBuilder();for(inti=0;i<values.length&&num>0;i++){while(num>=values[i]){num-=values[i];roman.append(symbols[i]);}}returnroman.toString();}}复杂度分析
时间复杂度
- 方法一:O(1)。无论输入数字多大,都只需要进行固定次数的操作(分解数字 + 4次switch判断)。
- 方法二:O(1)。最多循环13次(values数组长度),也是常数时间复杂度。
空间复杂度
- 两种方法都是 O(1),只使用了常数级别的额外空间。
测试用例
publicclassTest{publicstaticvoidmain(String[]args){Solutionsolution=newSolution();// 测试用例System.out.println("3 -> "+solution.intToRoman(3));// IIISystem.out.println("4 -> "+solution.intToRoman(4));// IVSystem.out.println("9 -> "+solution.intToRoman(9));// IXSystem.out.println("58 -> "+solution.intToRoman(58));// LVIIISystem.out.println("1994 -> "+solution.intToRoman(1994));// MCMXCIVSystem.out.println("3999 -> "+solution.intToRoman(3999));// MMMCMXCIX}}常见问题解答
Q1: 为什么输入范围是 1-3999?
A: 罗马数字没有表示 0 的符号,且最大的常规罗马数字是 MMM(3000) + CM(900) + XC(90) + IX(9) = 3999。
Q2: 两种方法哪种更好?
A: 硬编码枚举法更直观易懂,适合面试和初学者理解。贪心算法更通用,如果规则变化更容易扩展。在实际编码中,硬编码法因为减少了循环,性能略优。
Q3: 如何处理边界情况?
A: 题目保证输入在有效范围内,但实际开发中可以添加输入验证:
if(num<1||num>3999){thrownewIllegalArgumentException("输入数字必须在 1-3999 范围内");}总结
LeetCode 12题"整数转罗马数字"是一道经典的字符串处理题目,主要考察:
- 对罗马数字规则的理解
- 数字的分解与组合能力
- 代码的清晰度和可读性
硬编码枚举法虽然看起来"简单粗暴",但在这道题中是最优解之一。它充分利用了题目限制(1-3999),将问题分解为四个独立的子问题,代码既高效又易于理解。
关键点:理解罗马数字的构成规则,特别是6种特殊的减法情况(IV, IX, XL, XC, CD, CM),这是解题的核心。
希望这篇题解对你有帮助!如果有任何疑问或建议,欢迎在评论区留言讨论。