当前位置: 首页 > news >正文

JAVA练习369- 整数转罗马数字

题目概览

七个不同的符号代表罗马数字,其值如下:

符号
I1
V5
X10
L50
C100
D500
M1000

罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:

  • 如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
  • 如果该值以 4 或 9 开头,使用减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (V) 减 1 (I):IV,9 是 10 (X) 减 1 (I):IX。仅使用以下减法形式:4 (IV),9 (IX),40 (XL),90 (XC),400 (CD) 和 900 (CM)。
  • 只有 10 的次方(I,X,C,M)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V),50 (L) 或 500 (D)。如果需要将符号附加4次,请使用减法形式

给定一个整数,将其转换为罗马数字。

示例 1:

输入:num = 3749

输出:"MMMDCCXLIX"

解释:

3000 = MMM 由于 1000 (M) + 1000 (M) + 1000 (M) 700 = DCC 由于 500 (D) + 100 (C) + 100 (C) 40 = XL 由于 50 (L) 减 10 (X) 9 = IX 由于 10 (X) 减 1 (I) 注意:49 不是 50 (L) 减 1 (I) 因为转换是基于小数位

示例 2:

输入:num = 58

输出:"LVIII"

解释:

50 = L 8 = VIII

示例 3:

输入:num = 1994

输出:"MCMXCIV"

解释:

1000 = M 900 = CM 90 = XC 4 = IV

提示:

  • 1 <= num <= 3999

来源:12. 整数转罗马数字 - 力扣(LeetCode)

解题分析

方法一:模拟(贪心算法)

这是最直观和常用的方法。核心思想是:每次都尽可能使用当前最大的罗马数字符号来表示剩余的数字

算法步骤:

  1. 预先定义两个数组:
    • values[]:按从大到小的顺序存储所有可能的“数字值”,包括常规符号(如 1000, 500, 100...)和特殊的减法形式(如 900, 400, 90...)。
    • symbols[]:存储与values[]一一对应的罗马数字字符串。
  2. 初始化一个结果字符串(如StringBuilder)。
  3. 从最大的数字值(values[0])开始遍历数组:
    • 如果当前数字num大于等于values[i],则将对应的symbols[i]追加到结果中,并从num中减去values[i]
    • 重复此步骤,直到num小于values[i],然后移动到下一个更小的值。
  4. num被减至 0 时,转换完成,返回结果字符串。

为什么可行?

因为罗马数字的表示规则本质上就是“贪心”的:对于任何给定的数字,总是优先使用能表示它的最大符号。预定义的数组已经包含了所有必要的减法形式(如 IV, IX, XL 等),确保了算法能正确处理 4 和 9 相关的边界情况。

复杂度分析:

class Solution { // 按从大到小的顺序定义所有可能的“值-符号”对 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"}; public String intToRoman(int num) { StringBuilder roman = new StringBuilder(); // 遍历每一个值 for (int i = 0; i < values.length; ++i) { // 当剩余数字大于等于当前值时,就使用对应的符号 while (num >= values[i]) { roman.append(symbols[i]); num -= values[i]; } // 如果数字已经减到0,可以提前结束(非必需优化) if (num == 0) { break; } } return roman.toString(); } }

代码说明:

示例推演(num = 3749):

  1. num=3749 >= 1000,追加 “M”,num=2749。
  2. num=2749 >= 1000,追加 “M”,num=1749。
  3. num=1749 >= 1000,追加 “M”,num=749。此时已得到 “MMM”。
  4. num=749 < 900 但 >= 500,追加 “D”,num=249。得到 “MMMD”。
  5. num=249 < 400 但 >= 100,追加 “C”,num=149。得到 “MMMDC”。
  6. num=149 >= 100,追加 “C”,num=49。得到 “MMMDCC”。
  7. num=49 < 90 但 >= 40,追加 “XL”,num=9。得到 “MMMDCCXL”。
  8. num=9 >= 9,追加 “IX”,num=0。得到最终结果 “MMMDCCXLIX”。

方法二:硬编码

这种方法利用了罗马数字表示法的确定性:对于给定的整数(1 ≤ num ≤ 3999),其千位、百位、十位、个位上的数字是确定的,且每个数位上的数字(0-9)对应的罗马数字组合也是固定的。因此,我们可以预先为每个数位上的所有可能数字(0-9)编码好对应的罗马数字字符串,然后通过简单的数学运算取出每一位的数字,拼接对应的字符串即可。

核心思路:

  1. 数位分离:将输入整数num分解为千位、百位、十位、个位四个数字。
  2. 查表映射:为每个数位预先定义一个长度为 10 的字符串数组,下标 0-9 分别对应数字 0-9 在该数位上的罗马数字表示(其中 0 对应空字符串)。
  3. 拼接结果:将四个数位对应的罗马数字字符串按顺序(千位、百位、十位、个位)拼接起来,即为最终结果。

算法步骤:

  1. 定义四个字符串数组:
    • thousands[]:千位数字 0-3 对应的罗马数字(0 为空字符串,1 为 "M",2 为 "MM",3 为 "MMM")。
    • hundreds[]:百位数字 0-9 对应的罗马数字(例如 0="", 1="C", 2="CC", 3="CCC", 4="CD", 5="D", 6="DC", 7="DCC", 8="DCCC", 9="CM")。
    • tens[]:十位数字 0-9 对应的罗马数字(例如 0="", 1="X", 2="XX", 3="XXX", 4="XL", 5="L", 6="LX", 7="LXX", 8="LXXX", 9="XC")。
    • ones[]:个位数字 0-9 对应的罗马数字(例如 0="", 1="I", 2="II", 3="III", 4="IV", 5="V", 6="VI", 7="VII", 8="VIII", 9="IX")。
  2. 通过整数除法和取余运算获取每一位的数字:
    • 千位:num / 1000
    • 百位:(num % 1000) / 100num % 1000 / 100
    • 十位:(num % 100) / 10num % 100 / 10
    • 个位:num % 10
  3. 根据每一位的数字作为下标,从对应的数组中取出罗马数字字符串,依次拼接到结果中。
  4. 返回拼接后的字符串。

为什么可行?

因为罗马数字的表示是按位独立的。千位只由 'M' 组成,百位由 'C', 'D', 'M' 的组合构成,十位由 'X', 'L', 'C' 的组合构成,个位由 'I', 'V', 'X' 的组合构成。每个数位上的数字 0-9 都有唯一确定的罗马数字表示(包括减法形式如 IV, IX, XL, XC, CD, CM),且不同数位之间的表示不会相互干扰。因此,预先编码所有可能性并查表拼接是完全正确的。

复杂度分析:

class Solution { // 千位:0-3 对应空字符串、"M"、"MM"、"MMM" String[] thousands = {"", "M", "MM", "MMM"}; // 百位:0-9 对应的罗马数字 String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; // 十位:0-9 对应的罗马数字 String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; // 个位:0-9 对应的罗马数字 String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; public String intToRoman(int num) { // 使用 StringBuffer 或 StringBuilder 进行高效拼接 StringBuffer roman = new StringBuffer(); // 获取千位数字并拼接对应字符串 roman.append(thousands[num / 1000]); // 获取百位数字并拼接对应字符串 roman.append(hundreds[num % 1000 / 100]); // 获取十位数字并拼接对应字符串 roman.append(tens[num % 100 / 10]); // 获取个位数字并拼接对应字符串 roman.append(ones[num % 10]); return roman.toString(); } }

代码说明:

示例推演(num = 3749):

  1. 千位:3749 / 1000 = 3thousands[3] = "MMM"
  2. 百位:3749 % 1000 = 749749 / 100 = 7hundreds[7] = "DCC"(注意:700 是 DCC,不是 CC...)
  3. 十位:3749 % 100 = 4949 / 10 = 4tens[4] = "XL"
  4. 个位:3749 % 10 = 9ones[9] = "IX"
  5. 拼接:"MMM" + "DCC" + "XL" + "IX" = "MMMDCCXLIX"

方法对比:

http://www.jsqmd.com/news/1289231/

相关文章:

  • 3分钟搞定Windows与Office激活:KMS智能脚本全攻略
  • Joplin搜索终极指南:3分钟掌握笔记查找的魔法
  • 留学生健康诊断证明翻译怎么办理?去哪里办理翻译手续? - 点办通
  • 告别刻录盘!WinCDEmu让Windows镜像挂载如此简单
  • GetQzonehistory:3分钟学会永久保存QQ空间十年青春记忆的终极免费工具
  • 2026年 无锡卧式干法球磨机生产厂家:高耐磨/低能耗/环保型设备,品质与实力解析 - 优企名品
  • Windows RTMP直播服务器终极指南:5分钟快速搭建专业流媒体平台
  • OpCore-Simplify终极指南:15分钟完成Hackintosh自动配置的神器
  • 一篇旧文章如何在三年后继续误导 AI:GEO 内容时效性的维护日志
  • 破解研究M8游戏棒,东西不错,就是坑有点大
  • Java static与final关键字深度解析与实战应用
  • 终极指南:PotatoNV深度解析 - 麒麟芯片Bootloader解锁的完整解决方案
  • 成人学历学信网终身可查,全国通用 - 学历提升信息早知道
  • 最新AI+CMIP6数据分析与可视化、降尺度技术与气候变化的区域影响、极端气候分析
  • 如何15分钟完成OpenCore自动化配置:终极智能引擎简化Hackintosh搭建
  • 南京大学 操作系统 (JYY) 学习笔记:动态链接的黑魔法与内存入侵 (Dynamic Linking)
  • 2026年07月反光安防行业优质制造厂家与供货商全景观察 - 优企名品
  • Starccm浮式风机CFD仿真与七自由度运动分析
  • Ventoy:一个U盘装下所有系统!告别反复格式化的终极启动盘方案
  • Windows下MinIO对象存储安装配置全指南
  • 奇摩技术说:大模型时代的工作流新范式 - 奇摩-workbuddy
  • 猫抓插件:三步解锁网页资源下载新体验
  • Edge-TTS语音合成:如何绕过微软限制实现跨平台免费TTS服务
  • 基于STELLA系统动态模拟技术及在农业、生态及环境等科学领域中的应用
  • 2026年7月目前靠谱的真空袋直销厂家推荐,服装自粘袋/食品袋/加厚平口袋/肉类真空袋/立体风琴袋,真空袋企业怎么选择 - 品牌推荐师
  • ★大润发购物卡回收几折最划算?2026?★ - 沃卡回收
  • 如何在安卓设备上快速实现摄像头视频替换:终极免费指南
  • 保险理赔AI化转型白皮书(2024监管合规版):覆盖92%拒赔争议场景的NLP+规则引擎双模架构
  • 南京大学 操作系统 (JYY) 学习笔记:从 UNIX 到 Linux 与庞大的应用生态
  • 猫抓浏览器扩展:终极网页媒体资源嗅探与下载解决方案