罗马数字转整数
- 题目
- 函数原型
- 边界判断
- 算法设计:查表法
- 算法设计:模拟法
题目
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 ,即为两个并列的 1。12 写做 ,即为 。 27 写做 , 即为 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 ,而是 。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 。这个特殊的规则只适用于以下六种情况:
- 可以放在 (5) 和
- 可以放在 (50) 和
- 可以放在 (500) 和
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
示例 1:
输入: "III"
输出: 3
示例 2:
输入: "IV"
输出: 4
示例 3:
输入: "IX"
输出: 9
示例 4:
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
函数原型
C的函数原型:
int romanToInt(char * s){}
边界判断
题目里有说,输入确保在 1 到 3999 的范围内。
不过这个怎么判断呢,用户输入的是罗马数字,而不是阿拉伯数字…
你问我,我也不知道啊。
但可以对输入参数做检查。
if( s == NULL || *s == '\0') // 指针是否为NULL
return 0;
算法设计:查表法
因为只包含了 7 个数字,我们可以建一个表来映射罗马数字与阿拉伯数字之间的关系。
int map[90] = {'\0'};
// 建表, 来映射数字间的关系;大写字母的范围是 65(A)-90(Z)
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
建表
map['I'] = 1;
map['V'] = 5;
map['X'] = 10;
map['L'] = 50;
map['C'] = 100;
map['D'] = 500;
map['M'] = 1000;
读了示例后,发现罗马数字主要有俩种情况(从左往右看):
- 左加:左的数字(前一个数)比右边的数字(后一个数)大、相等时,加上前一个数
- 右减:左的数字(前一个数)比右边的数字(后一个数)小时,减去前一个数
例如,。
,[前一个数] 比 [后一个数] 小,就要减掉前一个数。
int romanToInt(char * s){
if( s == NULL || *s == '\0') // 指针是否为NULL
return 0;
int map[90] = {'\0'};
// 建表, 来映射数字间的关系
map['I'] = 1;
map['V'] = 5;
map['X'] = 10;
map['L'] = 50;
map['C'] = 100;
map['D'] = 500;
map['M'] = 1000;
int Roman_val = 0; // 定义一个变量,保存罗马数字转换后的值
// 俩种情况,分别讨论
for(int i=0; i<strlen(s); i++) // 从左往右看
if( map[s[i]] >= map[s[i+1]] ) // 左加(前一个数 >= 后一个数)
Roman_val += map[s[i]];
else // 右减(前一个数 < 后一个数)
Roman_val -= map[s[i]];
return Roman_val;
}
AC。
查表法的复杂度:
- 时间复杂度:
- 空间复杂度:
查表法是使用空间换时间,这个题目需要的空间很小,毕竟只有 7 种状态。
而大部分能使用查表法的题目,可能就需要大量的空间。虽然会让时间复杂度很好,但占用内存太多了。
在时间复杂度上,查表是 。
但是,在真实的计算机中,内存和处理器之间还有一个高速缓存,程序和数据要先从内存进入高速缓存,才能运行。
高速缓存的空间非常有限,通常只有几兆(),查表占用的内存空间可能是缓存容量的上千倍,这肯定是放不下的,遇到这种情况,计算机本身要进行上千次额外操作,把内存的内容往缓存倒腾。
也就是说,如果建立一个大表,虽然查表只需要做一次,但是准备工作可能要做上千次。
其实划不来,当然,也有一种补救的方法。
把一张大表拆分为几个小表,每个表查找一次,再把几次的相加,虽然查找次数多了,但占用的内存就很少的,这样就即有查表法的优点(查表时间复杂度是 ),又补救了占用大量内存空间的缺陷。
算法设计:模拟法
研究【右减】的情况,前一个数比后一个数小,就要减掉前一个数。
而这个规则只适用于以下六种情况:
- 可以放在 (5) 和
- 可以放在 (50) 和
- 可以放在 (500) 和
如果第 个元素是 ,第 个元素比第 个元素大,那就只有 。
如果第 个元素是 ,第 个元素比第 个元素大,那就只有 。
如果第 个元素是 ,第 个元素比第 个元素大,那就只有 。
int romanToInt(char * s){
int count = 0;
while (*s){
// 左加(前一个数比后一个数大)
if (*s == 'V') count += 5;
else if (*s == 'L') count += 50;
else if (*s == 'D') count += 500;
else if (*s == 'M') count += 1000;
// 右减(前一个数比后一个数小,就要减掉前一个数)
else if (*s == 'I')
count = (*(s + 1) == 'V' || *(s + 1) == 'X') ? count - 1 : count + 1;
else if (*s == 'X')
count = (*(s + 1) == 'L' || *(s + 1) == 'C') ? count - 10 : count + 10;
else if (*s == 'C')
count = (*(s + 1) == 'D' || *(s + 1) == 'M') ? count - 100 : count + 100;
s++;
}
return count;
}
过程模拟复杂度:
- 时间复杂度:
- 空间复杂度: