刷leetCode算法题+解析(三)

呃呃呃,一个愉快的周末过去了,接下来又是愉快的周一。刷题继续。

我这里再解释一下:任何解题的思路和方法都是多种多样的!我的习惯就是一种是自己想出来的,可能很墨迹,很累赘,性能又不好!但是完全是自己想的,所以我这里也会贴出来,而且附上完整的想法思路啥的。然后我一般还会贴一种大神的解法,这个其实我是在重多题解中选择我认为比较好的一种来分析,学习并分享出。有的方法可能我没写,但是也是对的,然后这个解析也是我认为思路好或者简洁性能优的,不一定就是最好的 ,别杠我!

回文数

题目:判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?

看到这个题大笑三声,秒有思路。感觉LeetCode上的题挺有意思的,不太相信是故意的,只能说巧的很,上一道题才看了整数反转,结果做这个题怕不是手到擒来呦~!然后因为很简单所以直接上代码了:

    public boolean isPalindrome(int x) {
        //首先我这是做了整数反转后再做这个题的。
        //整数分三类,正整数,负整数,0、0天然就是回文数,正整数有可能是回文数,负整数肯定不是回文数
        if(x<0){
            return false;
        }
        if(x==0){
            return true;
        }
        //思路是将这个数反转,如果等于原数则是回文数
        if(x>0){
            long result = 0l;
            //因为下面操作后x会改变,所以提前保存x的值,并且为了方便比较,直接设定为long型,不要吐槽我命名
            long xx = x;
            while(x!=0){
                result = result*10+x%10;
                x=x/10;
            }
            //原数是int。如果反转后溢出了则肯定是不是回文数
            if(result>Integer.MAX_VALUE||result<Integer.MIN_VALUE){
                return false;
            }
            if(result==xx){
                return true;
            }else{
                return false;
            }
        }
        return false;
    }

嗯,学到既得到,感谢上一个整数反转的题目。

罗马数字转整数

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 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 的范围内。
示例 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.

思路:首先说一下这个题,仔细看题思路很简单!!然后磕磕绊绊做了出来,实现是实现了,但是肯定不是优解!(这个就很无力了,然后我没想到,但是猜都能猜到又是人家几行的代码我写了几十行)

我自己的方法提交结果

public int romanToInt(String s) {
        int i = 0;
        if(s.indexOf("IV")!=-1){
            s = s.replace("IV", "");
            i += 4; 
        }
        if(s.indexOf("IX")!=-1){
            s = s.replace("IX", "");
            i += 9;
        }
        if(s.indexOf("XL")!=-1){
            s = s.replace("XL", "");
            i += 40;
        }
        if(s.indexOf("XC")!=-1){
            s = s.replace("XC", "");
            i += 90;
        }
        if(s.indexOf("CD")!=-1){
            s = s.replace("CD", "");
            i += 400;
        }
        if(s.indexOf("CM")!=-1){
            s = s.replace("CM", "");
            i += 900;
        }
        String[] arr = s.split("");
        for(String str:arr){
           if(str.equals("I")){
               i += 1;
           }
            if(str.equals("V")){
               i += 5;
           }
            if(str.equals("X")){
               i += 10;
           }
            if(str.equals("L")){
               i += 50;
           }
            if(str.equals("C")){
               i += 100;
           }
            if(str.equals("D")){
               i += 500;
           }
            if(str.equals("M")){
               i += 1000;
           }
        }
        return i;
    }

接下来是看大佬的思路,怎么说呢,首先我这里的if,普遍都换成了switch,其次我这里是真的很麻烦的处理,一个字符串来来回回遍历好几遍,而且最后又是if来回走。而大佬的思路就简单粗暴的多了,判断每一个数字,前面的比后面的小则是减。并且我刚刚测试了,用char类型比用string类型的比较性能好6ms,所以改完后完整版如下:

class Solution {
    public int romanToInt(String s) {
        int result = 0;
        //因为里面要用到当前元素的后一个字符,所以只能遍历到倒数第二个元素。
        for(int i=0;i<s.length()-1;i++){
            //前一个数字大于后一个数字则是正常相加
            if(getValue(s.charAt(i))>=getValue(s.charAt(i+1))){
                result += getValue(s.charAt(i));
            }else{
                 //前一个小于后一个则是减法
                result -= getValue(s.charAt(i));
            }
        }
        //最后一个元素一定是要相加的
        result += getValue(s.charAt(s.length()-1));
        return result;
        

    }
    public int getValue(char value){
        int result = 0;
        switch(value){
            case 'I':
               result = 1;
               break;
            case 'V':
               result = 5;
                break;
            case 'X':
               result = 10;
                break;
            case 'L':
               result = 50;
                break;
            case 'C':
               result = 100;
                break;
            case 'D':
               result = 500;  
                break; 
            case 'M':
               result = 1000;
                break;
        }
        return result;
    }
}

其实这个题目本身没什么好说的,但是好多语法上的细节可以聊聊,我提交了很多遍,有的思路差不多,但是一点不起眼的改动就让执行速度和性能变了很多。


最终版

之前这个for循环里的if-else我是用两个if来判断的,结果速度跑出来在百分之七十几左右,然后我看人家大神代码,方法差不多一样,为什么人家百分之九十九呢?区别就在于我这个两个if判断,人家一个fi-else选择,然后我只改了这一块,再执行少了两秒,超过了百分之二十多的人。
其实是想不明白这样if-else为什么性能好么?能想明白的,这样相当于一次只需要一次判断,而我那种写法每次需要两次判断的,但是我为什么还那么写?没注意,没想到而已。其实真的一直喊着性能,时间复杂度,优化之类的,但是又有多少落到实际上了呢?
很感谢这个LeetCode平台,尤其是每次提交都会弹出那个执行时间,消耗性能,把一种很抽象的说法量化了起来,也让我认识的更深。

最长公共前缀

题目:编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。

示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。

思路:这个题注意审题吧,反正我是做笑了。然后开始分析思路:一个string数组,获取公共前缀,其实第一印象感觉应该挺好做的,然后大体就是获取每一个字符串的第一位比较,相同往下比较,不相同直接返回。可是思路是有了,实践起来一步一个问题。首先空数组怎么办?其次每个字符串长度不同,怎么获取最短长度?最后一步才是双循环挨个对比。我习惯于在代码中注解写的很墨迹,所以没思路的可以看看注解

  public String longestCommonPrefix(String[] strs) {
        int i = strs.length;
        //如果这是一个空数组则直接返回空
        if(i==0){
           return "";
        }
        //获取最短的那个字符串的长度
        int j = strs[0].length();
        for(String s:strs) {
            if(j>s.length()) {
                //如果长度比j短则替换j
                j=s.length();
            }
        }
        //结果集
        String result = "";
        //遍历第一层,从字符串下表为0的字符开始比对
        for(int k=0;k<j;k++) {
            //第一个字符串的第一个字符,作为参照物
            char first = strs[0].charAt(k);
            //因为第一个字符串已经取出来做参照物了,所以直接从第二个字符串开始遍历
            for(int l=1;l<i;l++) {
                //如果进到if里说明已经不相等了。所以可以直接返回了。
                if(strs[l].charAt(k)!=first) {
                    return result;
                }
            }
            //能走到这一步说明第K个字符比对成功,是公共前缀,所以结果集中加上该字符
            result+=first;              
        }
        //通常情况下在上面for循环中的return中就返回了,能走到这步说明其中某个字符串全部都是公共前缀
        return result;                
    }

做到这里下一步就是看大神的思路和解析:一个神思维的大佬解析,就是人家几行我几十行代码的再一次重现。
我做了这么多乱七八糟的判断,人家大神都不用。而且思路简单,不会让人看了觉得迷茫,只会感叹我怎么想不出来这种做法???
简单说一下,就是判断数组非空则取第一个字符串开始与所有剩下的匹配,匹配不上则把第一个字符串最后一位截取,继续匹配,再全匹配不上再截取。直至匹配上了,那么就是说这个前缀就是最长公共前缀了。哪怕没有最长公共前缀也不过是截取成了“”空串,并不违反结果要求的。
下面是代码:

    public String longestCommonPrefix(String[] strs) {
        if(strs.length==0){
            return "";
        }             
        //以第一个字符串为标板
        String str = strs[0];
        for(int i=1;i<strs.length;i++){
            while(strs[i].indexOf(str)!=0){
                str = str.substring(0,str.length()-1);                  
            }
        }
        return str;
    }
image.png

满打满算人家的逻辑代码也就四行。这就是思路的重要性。下一题!

有效的括号

题目:给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。有效字符串需满足:1.左括号必须用相同类型的右括号闭合。2.左括号必须以正确的顺序闭合。3.注意空字符串可被认为是有效字符串。

示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true

这个题怎么说呢,想了几分钟,做了几分钟,做出来一看100多ms,心都碎了,我还自我感觉思路挺好的呢!


心碎的截图,100ms

然后因为这个题比较简单,所以我至今不知道大神啥思维,说说我自己的这个拿不出手的思路吧。

    public boolean isValid(String s) {
        int len = 0;
        //当s.length()等于len,说明这次while中没有剔除括号,说明剩下的字符串无法简化了
        while(s.length()!=len){
           len = s.length();
           s = s.replace("[]", "");
           s = s.replace("()", "");
           s = s.replace("{}", "");
        }
        return "".equals(s)?true:false;
    }

如图吧,只要是有效的括号,肯定会有最内层的括号,所以讲内层的括号直接剔除(也有可能是并列的括号,反正直接剔除),然后再继续下一轮再剔除,如果是有效的格式则会被剔除成“”空串,如果不是有效的格式则会无可提出而不满足while的条件,跳出while。结果集如果是空则ture,不是空则false。
接下来!!!大神解析:
看个屁的大神解析,看了n久,性能好的代码贼麻烦,用的栈,真真正正的做到了几行代码写成几十行,我还是就这么性能差着吧。附上大神代码,反正我有点没耐心看了

class Solution {
    public boolean isValid(String s) {
        if (s.length() == 0)
            return true;
        if ((s.length() & 1) == 1)
            return false;
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            switch (s.charAt(i)) {
                case '(':
                case '[':
                case '{':
                    stack.push(s.charAt(i));
                    continue;
                case ')':
                    if (stack.isEmpty() || stack.pop() != '(')
                        return false;
                    continue;
                case ']':
                    if (stack.isEmpty() || stack.pop() != '[')
                        return false;
                    continue;
                case '}':
                    if (stack.isEmpty() || stack.pop() != '{')
                        return false;
                    continue;
            }
        }
        return stack.isEmpty();
    }
}

然后今天就这样吧,目标每日3——5题,今天做了四道。每天进步一点点嘛~
如果稍微帮到了你麻烦点个喜欢点个关注啥的,也祝大家工作顺顺利利!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容