Algorithm之路十二:Integer to Roman
来源:互联网 发布:南京办公软件培训班 编辑:程序博客网 时间:2024/06/05 03:48
题目:
给出一个整数,返回其对应的罗马数字,整数的范围是1-3999。
举例:
1 ---> "I"
1321 ---> "MCCCXXI"
思路:
- 相同的数字连写,所表示的数等于这些数字相加得到的数,如 Ⅲ=3;
- 小的数字在大的数字的右边,所表示的数等于这些数字相加得到的数,如 Ⅷ=8、Ⅻ=12;
- 小的数字(限于 Ⅰ、X 和 C)在大的数字的左边,所表示的数等于大数减小数得到的数,如 Ⅳ=4、Ⅸ=9;
还有一条规矩是不能出现四个或四个以上连续的相同字符,所以9,90,900这样的数字应表示成IX,XC,CM,并且向8,80,800这样的数字,只能写成VIII,LXXX,DCCC这样的形式,如IIX,XXC,CCM这样的表示方法是不对的,这样的表示方法没有办法读,也不符合逻辑。
经过以上分析,需要将小数字放在大数字坐标的情况,只有输入的整数中出现9时才会用到,那么只需要把是1-3999中各个数量级的9单独列出来就可以,看代码更容易理解。
代码:
public static String intToRoman(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};String ret = "";while(num != 0){for(int i = 0;i < 13;i++)if(num >= value[i]){ret += roman[i];num -= value[i];break;}}return ret;}
问题:
思路很清晰,但是上面的代码耗费的时间却很长,原因不仅仅是因为算法的原因,而主要是因为基层的运行方式决定的,下面是这道题中运行最快的大佬写的。
private static int[] nums = new int[]{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; private static String[] strings = new String[]{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; public String intToRoman(int num) { StringBuilder res = new StringBuilder(); for (int i = 0 ; i<nums.length; i++) { while (num >= nums[i]) { res.append(strings[i]); num -= nums[i]; } } return res.toString(); }
这个和我的思路没有什么不同,但是运行时间差了50ms左右。这是因为他所采用的容器的基层结构决定的,这个就和java的翻译有关了,然而本人身为菜鸟一枚,并不懂。
从我的代码中可以看出,num在任何一个数量级的时候,num >= nums[i]都是从i=0开始执行的,这样显然有多余的循环部分,比如说当前num=9,那么直接从个位数的部分开始找就可以了,没有必要从头开始。所以把代码更改下,果然快了一些。
public static String intToRoman(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};String ret = "";while(num != 0){if(num < 100 && num >= 10){for(int i = 5;i < 13;i++){if(num >= value[i]){ret += roman[i];num -= value[i];break;}}}else if(num < 10){for(int i = 9;i < 13;i++){if(num >= value[i]){ret += roman[i];num -= value[i];break;}}}else{for(int i = 0;i < 13;i++){if(num >= value[i]){ret += roman[i];num -= value[i];break;}}}}return ret;}
如果将数字的范围划分的再细一些,会减少迭代的次数,应该会更快一点,于是实验了一下,确实快了些。
public static String intToRoman(int num){String [] roman = {"M","CM","DCCC","DCC","DC","D","CD","CCC","CC","C","XC","LXXX","LXX","LX","L","XL","XXX","XX","X","IX","V","IV","I"};int [] value = {1000,900,800,700,600,500,400,300,200,100,90,80,70,60,50,40,30,20,10,9,5,4,1};String ret = "";while(num != 0){if(num < 100 && num >= 10){for(int i = 10;i < 23;i++){if(num >= value[i]){ret += roman[i];num -= value[i];break;}}}else if(num < 10){for(int i = 19;i < 23;i++){if(num >= value[i]){ret += roman[i];num -= value[i];break;}}}else{for(int i = 0;i < 23;i++){if(num >= value[i]){ret += roman[i];num -= value[i];break;}}}}return ret;}
以上两种改进方式纯属搞笑,如有雷同,不胜荣幸。
时间复杂度:
我的最初的代码中,到一个新的数量级,迭代的次数最大值是一定的。下面举例说明:
num = 3832,在千位数量级的时候,while循环最多三次之后,num就会降低一个数量级。
num = 832,在百位数量级的时候,while循环最多四次之后(因为800需要经过D,C,C,C之后才能转换成功),num就会降低一个数量级。
num = 32,在十位数量级的时候,while循环最多四次(当十位数为8的时候,while循环需要四次),num就会降低一个数量级。
...
根据以上分析,在1-3999之间的任何数,转换成罗马数字,最多只需要3+4+4+4共15次循环即可得出最终结果。也就是说可以在一个常数时间内完成运行。
所以时间复杂度为O(1)。
空间复杂度:
需要存储String数组和int数组,常数量空间,O(1)。
- Algorithm之路十二:Integer to Roman
- Algorithm之路十三:Roman to Integer
- 每日算法之十二:Roman to Integer
- 【Leetcode Algorithm】Roman to Integer
- LeetCode Algorithm #13 Roman to Integer
- leetcode之路013 Roman to Integer
- leetcode之路012 Integer to Roman
- LeetCode进阶之路(Integer to Roman)
- LeetCode进阶之路(Roman to Integer)
- LeetCode之路:13. Roman to Integer
- leetcode之Roman to Integer
- LeetCode之Roman to Integer
- leetcode 之 Roman to Integer
- leetCode 之 Roman to Integer
- 【Leetcode】之Integer to Roman
- 【Leetcode】之Roman to Integer
- leetcode之Roman to Integer
- LeetCode 之 Roman to Integer
- Java并发编程:深入剖析ThreadLocal
- hive-行转列列转行
- css基础———简介、语法、引用方式
- oracle数据类型转换
- 线程
- Algorithm之路十二:Integer to Roman
- 第三章 RDD编程
- JavaScript
- JavaScript基础(10.函数与序列化、转义)
- 干货 | 自然语言处理(2)之浅谈向量化与Hash-Trick
- Vxworks内存管理
- 区块链+AI+广告:不仅数据更透明真实,他们让内容的权益回归作者
- Customized display of collection data in a PropertyGrid
- 哈理工 oj1948我又回来了 【基础bfs】