這是悅樂書的第351次更新,第376篇原創
01 看題和準備
今天介紹的是LeetCode算法題中Medium級別的第6題(順位題號是12)。羅馬數字由七個不同的符號表示:I,V,X,L,C,D和M.
符號 值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如,2用羅馬數字寫成II
,由兩個I加在一起。12寫爲XII
,簡稱爲X + II
。
27寫成XXVII
,即XX + V + II
。
羅馬數字通常從左到右從最大到最小。但是,四個數字不是IIII
。相反,4寫爲IV
。
因爲I
在V
之前,我們做減法得到4。同樣的原則適用於9,9被寫爲IX
。有六個使用減法的實例:
I
可以放在V
(5)和X
(10)之前,得到4和9。
X
可以放在L
(50)和C
(100)之前,得到40和90。
C
可以放在D
(500)和M
(1000)之前,得到400和900。
給定一個整數,將其轉換爲羅馬數字。輸入保證在1到3999的範圍內。例如:
輸入:3
輸出:“III”
輸入:4
輸出:“IV”
輸入:9
輸出:“IX”
輸入:58
輸出:“LVIII”
說明:L = 50,V = 5,III = 3。
輸入:1994
輸出:“MCMXCIV”
說明:M = 1000,CM = 900,XC = 90,IV = 4。
02 第一種解法
題目要求我們將整數轉成羅馬數字符串來表示,和之前遇到的羅馬數字符串轉整數正好相反。
題目限定了num的範圍,[1,3999]
,針對常見的數字,列了下面這些羅馬數和整數的對應關係:
900 CM 90 XC 9 IX
800 DCCC 80 LXXX 8 VIII
700 DCC 70 LXX 7 VII
600 DC 60 LX 6 VI
500 D 50 L 5 V
400 CD 40 XL 4 IV
3000 MMM 300 CCC 30 XXX 3 III
2000 MM 200 CC 20 XX 2 II
1000 M 100 C 10 X 1 I
我們可以將他們的對應關係分爲4組:
第一組,最高位小於4,他們對應的羅馬數是疊加的,例如1000是M,3000是
MMM
。第二組,最高位等於4,他們對應的羅馬數是在以5開頭羅馬數前加上以1開頭的羅馬數,且兩者是相鄰的關係。
第三組,最高位在5到8之間,對應的羅馬數是以5開頭的羅馬數爲基礎,後跟上疊加的1開頭的羅馬數,兩者也是相鄰的關係。
第四組,最高爲等於9,對應的羅馬數是以1開頭的羅馬數間隔組合,例如900,在代表1000的
M
前加代表100的C
,即CM
,兩者中間間隔一位。
根據以上的對應關係組合,我們將不同整數、羅馬數字符串初始化爲數組,由高位到低位,依次計算羅馬數。
public String intToRoman(int num) {
String[] roman = {"M", "D", "C", "L", "X", "V", "I"};
int[] value = {1000, 500, 100, 50, 10, 5, 1};
StringBuilder sb = new StringBuilder();
for (int i=0; i<value.length; i+=2) {
// 得到當前num的最高位
int tem = num/value[i];
if (tem < 4) {
// 疊加
for (int j=0; j<tem; j++) {
sb.append(roman[i]);
}
} else if (tem == 4) {
// 相鄰
sb.append(roman[i]+roman[i-1]);
} else if (tem > 4 && tem < 9) {
// 相鄰
sb.append(roman[i-1]);
for (int j=6; j<=tem; j++) {
sb.append(roman[i]);
}
} else if (tem == 9) {
// 間隔兩位
sb.append(roman[i]+roman[i-2]);
}
// 去掉已經參與計算的高位
num = num%value[i];
}
return sb.toString();
}
03 第二種解法
在第一種解法的基礎上,我們將初始數組中的元素分別對應增加了6位,以9和4開頭。依舊是從高位往低位計算,與第一種解法中利用取餘、取整不同,此解法是循環做減法,直到num剩下的值小於當前需要減去的值,纔會尋找下一個需要減去的數。
public String intToRoman2(int num) {
String[] roman = {"M", "CM", "D", "CD", "C", "XC",
"L", "XL", "X", "IX", "V", "IV", "I"};
int[] value = {1000, 900, 500, 400, 100, 90,
50, 40, 10, 9, 5, 4, 1};
StringBuilder sb = new StringBuilder();
for (int i=0; i<value.length; i++) {
while (num >= value[i]) {
sb.append(roman[i]);
num -= value[i];
}
}
return sb.toString();
}
04 第三種解法
還有一種取巧的解法。因爲題目限定num的範圍,最高不會超過4000,我們可以把num拆成4個部分,千位只有四種可能,0(num是一個三位數)、1、2、3,百位對應的有10種可能,0到9,十位和個位也是10種可能,只需要將他們都列出來,依次取num中不同位對應的字符串即可。
此解法的時間複雜度是O(1)
。
public static String intToRoman3(int num) {
String M[] = {"", "M", "MM", "MMM"};
String C[] = {"", "C", "CC", "CCC", "CD",
"D", "DC", "DCC", "DCCC", "CM"};
String X[] = {"", "X", "XX", "XXX", "XL",
"L", "LX", "LXX", "LXXX", "XC"};
String I[] = {"", "I", "II", "III", "IV",
"V", "VI", "VII", "VIII", "IX"};
return M[num/1000] + C[(num%1000)/100] +
X[(num%100)/10] + I[num%10];
}
05 小結
算法專題目前已連續日更超過六個月,算法題文章219+篇,公衆號對話框回覆【數據結構與算法】、【算法】、【數據結構】中的任一關鍵詞,獲取系列文章合集。
以上就是全部內容,如果大家有什麼好的解法思路、建議或者其他問題,可以下方留言交流,點贊、留言、轉發就是對我最大的回報和支持!