阿拉伯数字转换为罗马数字 | JavaScript 算法实现

昨日,在 FCC 平台整整用了两三小时,才刷出一道 JS 算法题,回首而看,最终的代码也就那么多行,记录过程,写文以促进之后改进,也顺便整理一下自己在 FCC 上遇到问题后的解决思路。

阿拉伯数字转换为罗马数字 -- 做题传送门,来试试~

想看答案的可以直接拉至文章后方~~


数字转换规则


罗马数字我们都不陌生,IIIVIX 等字符见文知意,可是当阿拉伯数字达到百千级别的时候,用什么字母表示?字母的排列顺序又应该是什么样子的呢?下面诺列了几个规则:

基本字符

I、V、X、L、C、D、M

分别代表

1、5、10、50、100、500、1000

计数规则:

  • 若干相同数字连写表示的数是这些罗马数字的和,如III=3;
  • 小数字在大数字前面表示的数是用大数字减去小数字,如IV=4;
  • 小数字在大数字后面表示的数是用大数字加上小数字,如VI=6;

组合规则:

  • 基本数字Ⅰ、X 、C 中的任何一个,自身连用构成数目,或者放在大数的右边连用构成数目,都不能超过三个;放在大数的左边只能用一个。
  • 不能把基本数字 V 、L 、D 中的任何一个作为小数放在大数的左边采用相减的方法构成数目;放在大数的右边采用相加的方式构成数目,只能使用一个。
  • V 和 X 左边的小数字只能用 Ⅰ。
  • L 和 C 左边的小数字只能用 X。
  • D 和 M 左 边的小数字只能用 C 。

可见该字符只适用于 4000 以下的正整数阿拉伯数字。


思路历程


按照规则来说,看来也就是要满足类似如下输入和输出:

首先想到的是找不同字符:最终也就只有 I、V、X、L、C、D、M 这七个字符分别代表几个数字,进行一定的左右顺序排列后完成转换。那么在这里首先要生成一个装有七个元素的数组,这时输入 9 的话,去拼装 I 和 X,输入 97 的话,去拼装 L 或者 C 等等。

var Roman = {
  1: 'I',
  5: 'V',
  10: 'X',
  50: 'L',
  100: 'C',
  500: 'D',
  1000: 'M'
};

就像 97 ,选择从 L (50)向右拼装呢还是选择 C (100)向左拼装,看来要去找输入数字左右两侧有对应罗马字符的数字,例如 97 的左边有代表 'L' 的 50 ,右边有代表 'C' 的 100。

开始设计算法,处处断点跟踪。

function convert(num) {
    if (isNaN(num)) return num;
    var str = "";
    var left, right; // 找到输入数字的左值和右值
    var p;
    for (p in Roman) {
      if (num > p) {
        left = p; // 左边的值一直在跟踪
      } else {
        right = p; // 知道左边的值找到后,左边的值得下一个赋给右边的值
        break;
      }
    }
    console.log("(left: " + left + ")----" + num + "----(right: " + right + ")");
    return str;
}

断点跟踪情况来看。发现确实能找到输入数字左右两边的值。


变得更复杂了,及时收手,重新思考


哇塞,找到输入数字左右两个数字后呢?在之前的转换规则里有说过,最大的罗马数字(例如‘V’),左侧的数字(例如‘I’)代表减少,右侧的数字代表(例如‘II’)增加。这时,罗马数字‘IV’、‘VII’即为阿拉伯数字 4 和 7 了。

当这时只用单个字符左右拼装 3999 的时候,一切都复杂了。

3999 --> MMMCMXCIX

要判断 9 应该转换为 VIIII 还是 IX ,必然会多出很多选择循环分支,变得更复杂了,及时收手,重新查找规律。

原来,像 97 这样的数字,不必纠结于是从 L(50) 开始拼装还是从 C(100) 开始拼装,90 转换成 XC ,7 转换成 VII 即可。看了看别的数字,都是这个规律呢,于是制定出如下对应表,直接查找到 个十百千 的任何一个。

var Roman = [["","I","II","III","IV","V","VI","VII","VIII","IX"],
             ["","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"],
             ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"],
             ["","M","MM","MMM","  "," ","  ","   ","    ","  "]];

3999 就可以理解为:3000 -> MMM, 900 -> CM, 90 -> XC, 9 -> IX 了。

3999 --> MMMCMXCIX


最终算法实现


其实你只用从这里正式看就可以了,前面的情景铺设不利于直接解答你的困惑呢。最终实现功能的函数才只有短短的 8 行。

  • ReverseArr 数组用来将输入数字转换成字符数组后倒序保存,这样 ReverseArr[0] 永远是输入数字的个位数,便于处理。
  • CorrectArr 数组用来将每个 ReverseArr 数组的数字查表转换成对应的罗马数字。
  • 最终返回 CorrectArr 数组经过 join 方法后连接起来的字符串。
var Roman = [["","I","II","III","IV","V","VI","VII","VIII","IX"],
             ["","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"],
             ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"],
             ["","M","MM","MMM","  "," ","  ","   ","    ","  "]];

// 只能转换 4000 以下的正整数阿拉伯数字
function convert(num) {
    if(isNaN(num)) return num;
    var ReverseArr = num.toString().split("").reverse();
    var CorrectArr = [];
    for (var i = 0; i < ReverseArr.length; i++) {
      CorrectArr.unshift(Roman[i][ReverseArr[i]]);
    }
    return CorrectArr.join("");
}

俩小时后的这一瞬间,感动死我了思密达:


�整理自己的思考过程


  • 查看 MDN 文档(重要),MDN 文档由网上所有人参与编辑维护,里面有几乎全部的 JS 对象及其方法的使用用例。
  • 随时 Chrome 开发者工具测试 demo 和调试,处处 console.log “人肉”看状态。
  • 搜索引擎 -- 百度 or 谷歌(辅助),算法这块,到瓶颈之处真不好意思一直不查资料想下去。
  • FCC 微信群,记得“提问的智慧”哦,人人都很忙呢。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容