徒手挖地球六周目

徒手挖地球六周目

NO.13 罗马数字转整数 简单

Q4As2Q.png

Q4Ar8g.png

思路一:哈希表 1.用一个hash表把所有罗马数字和阿拉伯数字相互匹配的特殊值作为键值对存储起来,例如"M,1000"、“CM,900”、“D,500”、“CD,400”。。。2.然后将字符串逐步分割并去hash表进行查询匹配,因为两位长度的罗马数字优先于一位长度的罗马数字,所以每步循环都需要先两位分割匹配再一位分割匹配。3.匹配到hash表的键之后,将对应的值加入结果中。

public int romanToInt(String s) {
        int ans=0;
//        所有罗马数字和阿拉伯数字相互匹配的特殊值作为键值对存储起来
        Map<String,Integer> map=new HashMap<>();
        map.put("M",1000);
        map.put("CM",900);
        map.put("D",500);
        map.put("CD",400);
        map.put("C",100);
        map.put("XC",90);
        map.put("L",50);
        map.put("XL",40);
        map.put("X",10);
        map.put("IX",9);
        map.put("V",5);
        map.put("IV",4);
        map.put("I",1);
//        然后将字符串逐步分割并去hash表进行查询匹配
        for (int i=0;i<s.length(); ){
//            两位长度的罗马数字优先于一位长度的罗马数字,所以先进行两位长度罗马数字的判断
            if (i+1<s.length()&&map.containsKey(s.substring(i,i+2))){
                ans+=map.get(s.substring(i,i+2));
                i+=2;
            }else {
                ans+=map.get(s.substring(i,i+1));
                i++;
            }
        }
        return ans;
    }

时间复杂度:O(n)

NO.14 最长公共前缀 简单

Q5e8Nq.png

思路一:依次LCP法假设输入数组为[“leetcode”,“leetcodes”,“leetgo”,“letsgo”]:1. 我们先让0号元素和1号元素求公共前缀 LCP(0号元素,1号元素)求出最长公共前缀prefix=“leetcode”。2. 然后prefix=LCP[prefix,2号元素]求出prefix=leet。3. prefix=LCP[prefix,3号元素]求出prefix=le。求出最终的答案"le"。

public String longestCommonPrefix(String[] strs) {
//        如果数组元素个数为0,返回""
        if (strs.length==0)return "";
//        假设最长公共前缀是数组第一个元素
        String prefix=strs[0];
//        从前向后数组元素依次和当前最长公共前缀比较
        for (int i=1;i<strs.length;i++){
            //如果当前被比较的元素不含有当前最长前缀,就将当前最长前缀从后面减少一位
            while (strs[i].indexOf(prefix)!=0){
                prefix=prefix.substring(0,prefix.length()-1);
//                如果当前最长前缀已经为空,则说明数组中的元素没有公共前缀,直接返回""
                if (prefix.isEmpty())return "";
            }
        }
        return prefix;
    }

时间复杂度:O(s) s是数组中每个字符串的字符个数之和

空间复杂度:O(1)

思路二:优化依次LCP法 上述方法中,无论数组每个元素的长短我们都需要从第一个数组元素比较到最后一个数组元素,所以有一个明显的问题:如果数组中最后一个元素非常短,但是我们仍然需要比较s次。

可以将思路一的算法改为从前往后枚举字符串的每一列,先比较每个字符串相同列上的字符(即不同字符串相同下标的字符)然后再进行对下一列的比较。

依然假设输入数组为[“leetcode”,“leetcodes”,“leetgo”,“letsgo”]:1. 第一次循环i=0先用0号元素的第一个字符"l",去和1号元素的第一个字符比较,相等;继续去和第2号元素的第一个字符比较,相等;继续去和第3号元素的第一个字符比较,相等;2. 第二次循环i=1再用0号元素的第二个字符"e",去和1号元素的第二个字符比较,相等;继续去和第2号元素的第二个字符比较,相等;继续去和第3号元素的第二个字符比较,相等;3. 第三次循环i=2再用0号元素的第三个字符"e",去和1号元素的第三个字符比较,相等;继续去和第2号元素的第三个字符比较,相等;继续去和第3号元素的第三个字符比较,不相等,则返回数组0号元素[0,i)字符,即"le"。

public String longestCommonPrefix(String[] strs) {
        if (strs==null||strs.length==0)return "";
//        将数组的0号元素的每个字符逐一取出进行比较
        for (int i=0;i<strs[0].length();i++){
//            取出数组0号元素的i号字符
            char c = strs[0].charAt(i);
//            将c和数组的每个元素的i号字符进行比较
            for (int j=1;j<strs.length;j++){
//                如果某个元素的所有字符都已比较完毕(即最短的元素)或者某个元素的第i个字符和c不相等
                if (i==strs[j].length()||strs[j].charAt(i)!=c){
//                    则0号元素的[0,i)号字符就是公共前缀
                    return strs[0].substring(0,i);
                }
            }
        }
        return strs[0];
    }

时间复杂度:O(s) s是数组中每个字符串的字符个数之和。在最坏情况下,本算法的效率与算法一相同,但是最好的情况下,算法只需要进行 n*minLen 次比较,其中minLen是数组中最短字符串的长度。

空间复杂度:O(1)

思路三:分治法 其实这方法也是上述的依次LCP法的一种优化方法,LCP(s1,s2,…,sn)=LCP(LCP(s1,s2,…,smid),LCP(smid+1,smid+2,…sn))。

先将数组元素分成两部分,分别求LCP得到lcpLeft和lcpRight,最后求LCP(lcpLeft,lcpRight)得到prefix。

public String longestCommonPrefix(String[] strs) {
    if (strs == null || strs.length == 0) return "";    
        return longestCommonPrefix(strs, 0 , strs.length - 1);
}

private String longestCommonPrefix(String[] strs, int l, int r) {
    if (l == r) {
        return strs[l];
    }
    else {
        int mid = (l + r)/2;
        String lcpLeft =   longestCommonPrefix(strs, l , mid);
        String lcpRight =  longestCommonPrefix(strs, mid + 1,r);
        return commonPrefix(lcpLeft, lcpRight);
   }
}

String commonPrefix(String left,String right) {
    int min = Math.min(left.length(), right.length());       
    for (int i = 0; i < min; i++) {
        if ( left.charAt(i) != right.charAt(i) )
            return left.substring(0, i);
    }
    return left.substring(0, min);
}

时间复杂度:O(S),S是所有字符串中字符数量的总和,S=m∗n。

空间复杂度:O(m*log(n))

NO.15 三数之和 中等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wSpkRvS0-1576661830367)(https://s2.ax1x.com/2019/12/17/QocMLV.png)]

这道题有一个麻烦的地方,就是需要去重,如果直接简单的三重循环暴力破解的话,除了时间复杂度问题之外还不便于去重。

思路一:双指针法 1. 首先对数组进行排序。2. 依次遍历数组元素,每遍历一个元素nums[i]时,就用左右指针指向nums[i]后面元素部分的两端,即指向nums[L]和nums[R],判断nums[i]、nums[L]和nums[R]之和sum是否等于0,等于0则加入结果集。如果sum>0,则说明需要较小的数字,即"R–"。如果sum<0,则说明需要较大的数字,即"L++"。循环直至左右指针相遇,即后面元素部分已组合完毕,则本次循环结束。3. 如果遍历到某个元素nums[i]已经大于0,则三数之和必然大于0(充分利用排序后的特点,减少无用的比较),结束循环。

然后是该算法去重的思路:4. 如果nums[i]==nums[i-1],就会导致结果重复,所以应该跳过。5. 如果sum==0的时候,nums[L]==num[L+1]就会导致结果重复,所以应该跳过,L++。6. 如果sum==0的时候,nums[R]=nums[R-1]就会导致结果重复,所以应该跳过,R–。

public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans=new ArrayList<>();
        int len = nums.length;
        if (nums==null||len<3)return ans;
//        1.排序
        Arrays.sort(nums);
//        2. 依次遍历数组元素
        for (int i=0;i<len;i++){
//            如果当前元素已经大于0,那么之后所有的三数之和一定都大于0。结束循环。
            if (nums[i]>0)break;
//            4. 如果nums[i]==nums[i-1],就会导致结果重复,所以应该跳过。
            if (i>0&&nums[i]==nums[i-1])continue;
//            用左右指针指向nums[i]后面元素部分的两端
            int L=i+1;
            int R=len-1;
//            循环直至左右指针相遇,即后面元素部分已组合完毕,则本次循环结束。
            while (L<R){
                int sum = nums[i] + nums[L] + nums[R];
                if (sum==0){
//                    三数之和等于0,等于0则加入结果集。
                    ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
//                    5. 如果sum\=\=0的时候,nums[L]\=\=num[L+1]就会导致结果重复,所以应该跳过,L++。
                    while (L<R&&nums[L]==nums[L+1])L++;
//                    6. 如果sum\=\=0的时候,nums[R]=nums[R-1]就会导致结果重复,所以应该跳过,R--。
                    while (L<R&&nums[R]==nums[R-1])R--;
                    L++;
                    R--;
                }else if (sum>0){//如果sum>0,则说明需要较小的数字,即"R--"
                    R--;
                }else if (sum<0){//如果sum<0,则说明需要较大的数字,即"L++"
                    L++;
                }
            }
        }
        return ans;
    }

时间复杂度:O(n^2)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值