最近开始准备面试,算法是跑不了的。找到了一篇不错的文章
这个里面给出了两个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 beforeV
(5) andX
(10) to make 4 and 9.X
can be placed beforeL
(50) andC
(100) to make 40 and 90.C
can be placed beforeD
(500) andM
(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列表的第一题还是收获很多的。