LeetCode-hot100题解—Day2

原题链接:力扣热题-HOT100
题解的顺序和题目的顺序一致,那么接下来就开始刷题之旅吧,1-8题见LeetCode-hot100题解—Day1
注:需要补充的是,如果对于每题的思路不是很理解,可以点击链接查看视频讲解,是我在B站发现的一个宝藏UP主,视频讲解很清晰(UP主用的是C++),可以结合视频参考本文的java代码。

9.回文数

思路
一种思路是将x转换为字符串,然后采用双指针分别指向首尾来判断首尾元素是否相同来解决,进阶的解法是将x反转后与原来的x比较是否相同来解决,反转的思路和第7题相同。
时间复杂度
时间复杂度为O(n)
代码实现

class Solution {
    public boolean isPalindrome(int x) {
        //负数不是回文数
        if(x<0) return false;
        int num = x;
        int cur = 0;
        //反转x
        while(x!=0){
            if(cur > (Integer.MAX_VALUE - x % 10) / 10) return false;
            cur = cur * 10 + x % 10;
            x /= 10;
        }
        return num == cur;
    }
}

10.正则表达式匹配

思路
本题采用动态规划来解决,创建一个二维数组来记录匹配的结果,然后遍历两个字符串,这里p[j]有三种情况,分别为普通字符、'.''*',前两种情况很好确定状态方程,第三种情况的关键在于需要匹配多少个字符,具体的思路参考正则表达式匹配-详细思路
时间复杂度:
时间复杂度为O(mn)
代码实现

class Solution {
    public boolean isMatch(String s, String p) {
        int n= s.length();
        int m= p.length();
        //在s和p前面加空格,便于初始状态的初始化
        s = ' ' + s;
        p = ' ' + p;
        boolean[][] f = new boolean[n+1][m+1];
        f[0][0] = true;
        //将字符串转换为字符数组
        char[] cs = s.toCharArray();
        char[] cp = p.toCharArray();

        for(int i=0;i<=n;i++){
            for(int j=1;j<=m;j++){
                // 如果下一个字符是 '*',则代表当前字符不能被单独使用,跳过
                if (j + 1 <= m && cp[j + 1] == '*') continue;
                
                if (i - 1 >= 0 && cp[j] != '*') {
                    // 对应了 cp[j] 为普通字符和 '.' 的两种情况
                    f[i][j] = f[i - 1][j - 1] && (cs[i] == cp[j] || cp[j] == '.');
                } 
                if (cp[j] == '*') {
                    // 对应了 p[j] 为 '*' 的情况
                    f[i][j]=(j>=2 && f[i][j-2]) || (i>=1 && j>=2 && f[i-1][j-2] && (cs[i] == cp[j-1] || cp[j-1] == '.')) || (i>=2 && j>=2 && f[i-2][j-2] && ((cs[i-1]==cp[j-1] && cs[i]==cp[j-1]) || cp[j-1]=='.'));
                }
            }
        }
        return f[n][m];
    }
}

注:有一些测试用例没有通过,目前还没找到解决办法,如果有小伙伴有好的方法,欢迎留言讨论。后续如果我解决了的话会来更新的!

11.盛最多水的容器

思路
本题采用双指针来求解,由于盛水的体积为宽✖高,因此可以定义首尾两个指针,枚举出不同高度的体积并取最大值,在移动指针的过程中,应该移动高度较小的指针,因为盛水的高度是由短板来决的,如图,不同的颜色表示每次移动指针后得到的体积。视频讲解点击视频讲解-盛最多水的容器
在这里插入图片描述

时间复杂度
时间复杂度为O(n),空间复杂度O(1)
代码实现

class Solution {
    public int maxArea(int[] height) {
        int ans = 0;
        int l = 0;
        int r =height.length - 1;
        while(l < r){
            ans = Math.max(ans,Math.min(height[l],height[r]) * (r-l));
            if(height[l] < height[r]) l++;
            else r--;
        }
        return ans;
    }
}

12.整数转罗马数字

思路
读完题目可以理解罗马数字是怎么组成的,由于本题给出的范围是3999,比较小,所以一个最简单的方法是将需要用到的千位,百位,十位,个位数全部存到对应的数组中,然后通过分解给出的num来逐个匹配和拼接。如果不是很理解可以点击视频详解-整数转罗马数字
时间复杂度
这段代码的时间复杂度为O(1),因为无论输入的num大小如何,都只进行了一次计算,没有随输入规模增加而增加的循环或递归。
代码实现

class Solution {
    public String intToRoman(int num) {
        String[] thousands = new String[]{"","M","MM","MMM"};
        String[] hundreds = new String[]{"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"};
        String[] tens = new String[]{"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"};
        String[] ones = new String[]{"","I","II","III","IV","V","VI","VII","VIII","IX"};
        return thousands[num / 1000] + hundreds[num % 1000 /100] + tens[num % 100 / 10] + ones[num % 10];
    }
}

13.罗马数字转整数

思路
本题和12题正好相反,根据12题我们已经知道了罗马数字组成的规律了,我们只需要遍历给出的罗马数字,累加罗马数字对应的整数即可,注意需要加上判断,因为罗马数字中有一些特殊的数字,当当前的字符代表的整数小于后面字符对应的整数时,结果需要减去当前字符对应的整数(如,4是IV,是5-1),当当前字符对应的整数大于后一个字符对应的整数后,结果加上当前字符对应的整数,下图给出一个直观的例子。详细的视频讲解点击视频讲解-罗马数字转整数
在这里插入图片描述
时间复杂度
时间复杂度是O(n),其中n是字符串s的长度。
代码实现

class Solution {
    public int romanToInt(String s) {
        Map<Character,Integer> value =new HashMap<>();
        value.put('I',1);
        value.put('V',5);
        value.put('X',10);
        value.put('L',50);
        value.put('C',100);
        value.put('D',500);
        value.put('M',1000);
        int ans = 0;
        char[] cs = s.toCharArray();
        for(int i=0;i<s.length();i++){
            if(i+1 < s.length() && value.get(cs[i]) < value.get(cs[i+1])){
                ans -= value.get(cs[i]);
            }else{
                ans += value.get(cs[i]);
            }
        }
        return ans;
    }
}

14.最长公共前缀

思路
本题采用暴力解法,以第一个字符串为基础,与数组中其他字符串比较,依次取出第一个字符串的每个字符,与后面的字符串的字符比较,当遇到某个字符串的长度溢出或者当两个字符不匹配时,截取第一个字符串,最后返回改字符串数组中的第一个字符串即为所求,详细的视频讲解点击视频讲解-最长公共前缀
时间复杂度
这段代码的时间复杂度为O(n*m),其中n是字符串数组的长度,m是数组中最短字符串的长度。在最坏的情况下,需要比较所有字符串的每个字符,所以时间复杂度为O(n*m)
代码实现

class Solution {
    public String longestCommonPrefix(String[] strs) {
        for(int i = 0;i < strs[0].length(); i++){
            char c = strs[0].charAt(i);
            for(int j = 1;j < strs.length;j++){
                if(i == strs[j].length() || strs[j].charAt(i) != c){
                    return strs[0].substring(0,i);
                }
            }
        }
        return strs[0];
    }
}

15.三数之和

思路
本题采用枚举+双指针的做法,外循环对nums的元素依次遍历,在遍历时寻找可以和外循环元素相加为0的元素对,内层循环采用首尾双指针进行遍历,但是这样做的前提是需要首先对数组进行排序,最后需要注意由于不能输出重复的结果,所以每次遍历时都要跳过相同元素。详细的讲解参考视频讲解-三数之和
时间复杂度
这段代码的时间复杂度为O(n^2),其中n为数组nums的长度。主要的时间复杂度来源于两层循环,外层循环遍历数组nums,内层循环使用双指针法遍历数组中的剩余元素。在最坏情况下,内层循环的时间复杂度为O(n),所以总的时间复杂度为O(n^2)
代码实现

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        //对数组进行排序,方便采用首尾指针进行遍历
        Arrays.sort(nums);
        for(int i=0;i<nums.length;i++){
            //跳过重复元素
            if(i > 0 && nums[i] == nums[i-1]) continue;
            int l = i + 1;
            int r = nums.length - 1;
            int target = 0 - nums[i];
            while(l < r){
                if(nums[l] + nums[r] == target){
                    ans.add(Arrays.asList(nums[i],nums[l],nums[r]));
                    //跳过重复元素
                    while(l < r && nums[l] == nums[ l + 1]) l++;
                    while(l < r && nums[r] == nums[r - 1]) r--;
                    l++;
                    r--;
                }else if(nums[l] + nums[r] < target){
                    l++;
                }else{
                    r--;
                }
            }
        }
        return ans;
    }
}

知识拓展Java中动态数组的使用(ArrayListList
区别
1.ListArrayList的泛型等效类,List是一个接口,而ArrayList是一个类,它实现了List接口,所以List不能被构造,List list=new List()这种写法是错误的,而ArrayList就可以被构造。List list = new ArrayList();这句创建了一个ArrayList的对象后把向上转型成了List。此时它是一个List对象了,有些ArrayList有但是List没有的属性和方法,它就不能再用了。而ArrayList list=new ArrayList();创建一对象则保留了ArrayList的所有属性。
2.List相比ArrayList来说更加安全,因为ArrayList加入的数据为object类型,需要装箱和拆箱操作。List声明时就决定了类型,所以是类型安全的,省掉了装箱与拆箱的过程,并且效率更高。
用法
1.定义
ArrayList

ArrayList list1 = new ArrayList();

List

List<List<Integer>> list2 = new ArrayList<>();

2.添加元素
ArrayList
(1)添加单个元素

list1.Add(1);

(2)添加多个元素

list1.addAll(Arrays.asList(a,b,c));

List:
(1)添加单个元素

list2.Add(1);

(2)添加多个元素

list2.add(Arrays.asList(a,b,c));

3.返回
需要注意的是ArrayList直接返回结果会报错,如果添加了多个元素后返回list,则需要使用List定义(eg.15题中如果定义时使用ArrayList会报错)。具体的原因我还没搞清楚,如果有小伙伴了解的欢迎留言交流,后续如果了解了这块内容会来更新哒~

16.最接近的三数之和

思路
本题的思路和15题类似,只是将判断条件改为比较三个元素之和与目标元素的差值的最小值,在求解时,我们还是先要将数组进行排序。详细的视频讲解参考视频讲解-最接近的三数之和
时间复杂度
时间复杂度为O(n^2),其中n为数组nums的长度。
代码中使用了三重循环,外层循环遍历数组元素,内层循环使用双指针lr进行夹逼求解。在最坏情况下,内层循环的执行次数为O(n),因此总体的时间复杂度为O(n^2)
排序数组的时间复杂度为O(nlogn),对数组进行排序只需要执行一次,不会影响总体的时间复杂度。
代码实现

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        int ans = nums[0] + nums[1] +nums[2];
        Arrays.sort(nums);
        for(int i = 0;i < nums.length;i++){
            int l = i + 1;
            int r = nums.length - 1;
            while(l < r){
                int sum = nums[i] + nums[l] + nums[r];
                if(Math.abs(sum - target) < Math.abs(ans - target)) ans = sum;
                if(sum < target){
                    l++;
                }else{
                    r--;
                }
            } 
        }
        return ans;
    }
}

待续…

  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘子味的小橙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值