Leetcode13 Roman to Integer

文章讲述了准备面试时遇到的LeetCode第13题,即罗马数字转阿拉伯数字的问题。作者分析了罗马数字的转换规则,提出了从左至右,遵循“左减右加”的原则进行计算。文章提供了两种Java实现,一种使用HashMap,另一种利用Switch/case和倒序遍历,后者在性能上有优势。作者还讨论了代码优化,包括减少空间使用和改进循环顺序。
摘要由CSDN通过智能技术生成

最近开始准备面试,算法是跑不了的。找到了一篇不错的文章

leetcode easy/medium list

这个里面给出了两个list,我就从第一个list开始做并且写写心得吧。

第一题是leetcode13的Roman to Integer

描述在leetcode13,简单来说就是罗马数字转阿拉伯数字。

对应关系在这里:

Symbol       Value
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

思路的话,通过例子去观察,其实比如罗马数字6和11很好表达分别是VI XI,但是比如4的规则不一样

  • I can be placed before V (5) and X (10) to make 4 and 9. 
  • X can be placed before L (50) and C (100) to make 40 and 90. 
  • C can be placed before D (500) and M (1000) to make 400 and 900.

通过观察其实可以大概看出来罗马数字符合左减右加的原则,既左边的数字小于右面的数字则右面的数字减去左边数字比如IV其实是5-1=4,左边的数字大于右面的数字则相加比如VI是5+1=6。然后用给出的测试用例看一下

Input: s = "MCMXCIV"
Output: 1994
Explanation: M = 1000, CM = 900, XC = 90 and IV = 4.

初始化total为0的话,变化是1000,900,1900,1890,1990,1989,1994

看起来阔以,我们开整:

核心逻辑:后一个字符对应的数字如果大于前一个相加,否则相减

if next>prev: total+=prev

else totoal-=prev

边界值:

其实这个想是不太会考虑全的,我这个脑子有限,总结下就是如果按照字符分割(字符放在hashMap里)每次的单值指针不能越界,所以最后一个如果是单的直接加上。

写出来如下,还是很好理解的。细节的话需要注意Map的key是Character,然后取的话使用charAt()方法。

class Solution {
    public int romanToInt(String s) {
        Map<Character, Integer> RomanIntegerReflectMap = new HashMap<>();
        RomanIntegerReflectMap.put('I', 1);
        RomanIntegerReflectMap.put('V', 5);
        RomanIntegerReflectMap.put('X', 10);
        RomanIntegerReflectMap.put('L', 50);
        RomanIntegerReflectMap.put('C', 100);
        RomanIntegerReflectMap.put('D', 500);
        RomanIntegerReflectMap.put('M',1000);
        int total = 0;
        for(int i=0; i<s.length(); i++){
            int preValue = RomanIntegerReflectMap.get(s.charAt(i));
            if(i<s.length()-1){
                int nextValue = RomanIntegerReflectMap.get(s.charAt(i+1));
                if(nextValue > preValue){
                    total = total - preValue;
                }else{
                    total = total + preValue;
                }
            }else{
                total = total + preValue;
            }

        }
        return total;
    }
}

直接写出来就通过了,看一下结果:

空间用的有点多,运行时间也还好,说明可以优化一下。然后我参照了solution里面的java高赞的方案 leetcode13 java solution ,我看到里面使用了Switch/case, 这个很不错因为HashMap的初始化默认也是占有16位的一个数组链表结构,而且使用Switch/case可以直接写在循环体里,不用每次在map里对应找了。然后作者用了4这个乘数去判断,避免了双指针的使用。还有循环顺序,貌似倒序for循环在更多数据的时候会有更好的性能表现,参照reverse loop has a better performance than forward one 。这样的话我们再修改下看看代码:

class Solution {
    public int romanToInt(String s) {
        int total = 0;
        int number = 0;
        for(int i=s.length()-1; i>=0; i--){
            switch(s.charAt(i)){
                case 'I': number=1; break;
                case 'V': number=5; break;
                case 'X': number=10; break;
                case 'L': number=50; break;
                case 'C': number=100; break;
                case 'D': number=500; break;
                case 'M': number=1000; break;
            }
            if(4 * number < total){
                total -= number;
            }else {
                total += number;
            }
        }
        return total;
    }
}

性能表现:

快多了,而且发现了使用-=要比直接写total = total - number省一些空间,还有在这个回答中作者没有使用括号只高于13%左右的用户,看起来括号有必要? 

在这个高赞的回答下有大神的补充,使用了新的变量代替了双指针的后一个指针,有点像临时变量去存储上一个结果,那么我们还能根据这个优化下。不过在我的机器上性能并没有提高,不过个人认为他这个方案更易读,而且继续发现了个省空间的方法,就是声明变量的时候,起码int的类型貌似会省空间,起码从这次测试结果来看,代码如下,注意prev=number:

class Solution {
    public int romanToInt(String s) {
        int total = 0, number = 0, prev = 0;
        for(int i=s.length()-1; i>=0; i--){
            switch(s.charAt(i)){
                case 'I': number=1; break;
                case 'V': number=5; break;
                case 'X': number=10; break;
                case 'L': number=50; break;
                case 'C': number=100; break;
                case 'D': number=500; break;
                case 'M': number=1000; break;
            }
            if(number < prev){
                total -= number;
            }else {
                total += number;
            }
            prev = number;
        }
        return total;
    }
}

性能表现如下:

 那就到这里了,easy列表的第一题还是收获很多的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值