打卡活动12-算法12. 整数转罗马数字

119 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目

罗马数字包含以下七种字符: IVXLCDM

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

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

示例 1:

输入: num = 3
输出: "III"

示例 2:

输入: num = 4
输出: "IV"

示例 3:

输入: num = 9
输出: "IX"

示例 4:

输入: num = 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.

示例 5:

输入: num = 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= num <= 3999

二、我的解答

第一次解答:

当我写了这么多行代码的时候 ,我就清楚地知道,我又垃圾了。

    class Solution {
   public static String intToRoman(int num) {
        int m = 0, d = 0, c = 0, l = 0, x = 0, v = 0, i = 0;
        int cm = 0;//cm 900
        int cd = 0;//CD
        int xc = 0;//XC 90
        int xl = 0;//XL
        int ix = 0;//IX 9
        int iv = 0;//IV
        m = num / 1000;
        int left = num - m * 1000;
        if (left / 900 > 0) {
            cm = left / 900;
            left = left - 900 * cm;
        } else {
            d = left / 500;
            left = left - 500 * d;
        }

        if (left / 400 > 0) {
            cd = left / 400;
            left = left - 400 * cd;
        } else {
            cd = left / 100;
            left = left - 100 * cd;
        }

        if (left / 90 > 0) {
            xc = left / 90;
            left = left - 90 * xc;
        } else {
            l = left / 50;
            left = left - 50 * cd;
        }

        if (left / 40 > 0) {
            xl = left / 40;
            left = left - 40 * xl;
        } else {
            l = left / 10;
            left = left - 10 * cd;
        }

        if (left / 9 > 0) {
            ix = left / 9;
            left = left - 9 * ix;
        } else {
            l = left / 5;
            left = left - 5 * cd;
        }

        if (left / 4 > 0) {
            iv = left / 4;
        } else {
            l = left;
        }
        String[] display = new String[]{"M", "CM", "CD", "D", "C", "XC", "XL", "L", "X", "V", "IX", "IV", "I"};
        int[] values = new int[]{m, cm, cd, d, c, xc, xl, l, x, v, ix, iv, i};
        String result = "";
        for (int i1 = 0; i1 < values.length; i1++) {
            for (int i2 = 0; i2 < values[i1]; i2++) {
                result=result+display[i1];
            }
        }
        return result;
    }
}

报错:解答错误

最后执行的输入:3

第二次解答:

class Solution {
   public static String intToRoman(int num) {
        int m = 0, d = 0, c = 0, l = 0, x = 0, v = 0, i = 0;
        int cm = 0;//cm 900
        int cd = 0;//CD
        int xc = 0;//XC 90
        int xl = 0;//XL
        int ix = 0;//IX 9
        int iv = 0;//IV
        m = num / 1000;
        int left = num - m * 1000;
        if (left / 900 > 0) {
            cm = left / 900;
            left = left - 900 * cm;
        } else {
            d = left / 500;
            left = left - 500 * d;
        }

        if (left / 400 > 0) {
            cd = left / 400;
            left = left - 400 * cd;
        } else {
            c = left / 100;
            left = left - 100 * c;
        }

        if (left / 90 > 0) {
            xc = left / 90;
            left = left - 90 * xc;
        } else {
            l = left / 50;
            left = left - 50 * l;
        }

        if (left / 40 > 0) {
            xl = left / 40;
            left = left - 40 * xl;
        } else {
            x = left / 10;
            left = left - 10 * x;
        }

        if (left / 9 > 0) {
            ix = left / 9;
            left = left - 9 * ix;
        } else {
            v = left / 5;
            left = left - 5 * v;
        }

        if (left / 4 > 0) {
            iv = left / 4;
        } else {
            i = left;
        }
        String[] display = new String[]{"M", "CM", "CD", "D", "C", "XC", "XL", "L", "X", "V", "IX", "IV", "I"};
        int[] values = new int[]{m, cm, cd, d, c, xc, xl, l, x, v, ix, iv, i};
        String result = "";
        for (int i1 = 0; i1 < values.length; i1++) {
            for (int i2 = 0; i2 < values[i1]; i2++) {
                result=result+display[i1];
            }
        }
        return result;
    }
}

三、系统解答

算法的重要性就是避免写出我那么臃肿的代码吧。什么ifelse无穷嵌套,双for循环甚至三层循环无限进入。进阶吧菜鸟!

方法一:模拟

根据罗马数字的唯一表示法,为了表示一个给定的整数num,我们寻找不超过num 的最大符号值,将num 减去该符号值,然后继续寻找不超过num 的最大符号值,将该符号拼接在上一个找到的符号之后,循环直至num 为0。最后得到的字符串即为num 的罗马数字表示。

编程时,可以建立一个数值-符号对的列表valueSymbols,按数值从大到小排列。遍历

valueSymbols 中的每个数值-符号对,若当前数值value 不超过num,则从num 中不断减去value,直至num 小于value,然后遍历下一个数值-符号对。若遍历中num 为0 则跳出循环。

代码

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) {
        StringBuffer roman = new StringBuffer();
        for (int i = 0; i < values.length; ++i) {
            int value = values[i];
            String symbol = symbols[i];
            while (num >= value) {
                num -= value;
                roman.append(symbol);
            }
            if (num == 0) {
                break;
            }
        }
        return roman.toString();
    }
}

复杂度分析

  • 时间复杂度:O(1)。由于valueSymbols 长度是固定的,且这13 字符中的每个字符的出现次数均不会超过3,因此循环次数有一个确定的上限。对于本题给出的数据范围,循环次数不会超过15 次。

  • 空间复杂度:O(1)。

方法二:硬编码数字

class Solution {
    String[] thousands = {"", "M", "MM", "MMM"};
    String[] hundreds  = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
    String[] tens      = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
    String[] ones      = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};

    public String intToRoman(int num) {
        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();
    }
}
  • 时间复杂度:O(1)。计算量与输入数字的大小无关。

  • 空间复杂度:O(1)。