LeetCode刷题第3周小结

79be54a5fca94053afb10d2feabe641a.png

 前言

        最近羊了个羊火爆全网,我也不知道这游戏为啥这么火啊,感觉就是低化版的消消乐,第一关幼儿园,第二关直接清华北大,游戏道具图案随机出现,能通关几乎只能靠运气加成,和数学逻辑没有太大关系,消耗的是巨大的时间。背后的底层套路是设置噱头吸引巨大流量,接入广告,所以这款微信小游戏背后的运营者简直赢麻了!

        扯远了,扯远了,羊了个羊也就估计火个两三周,但刷了个题还是得坚持玩啊,每日一题就像每日一关,力扣几千关等待各位扣友一起去AC!这是坚持刷题的第三周,期初是和朋友一起互相监督刷题、选题,如今已经成为每日的一种习惯,带着配备无限键盘的平板,再下载力扣APP,上无聊的杂课时能随时随地拿出来AC一道easy题,虽然现在自己的算法水平还是蛮菜的,但“骐骥一跃,不能十步;驽马十驾,功在不舍!”  坚持总归有所收获的!!

文章目录

 1.重塑矩阵

2.最长和谐子序列

3.验证回文串

4.第三大的数

5.加一

6.汇总区间

7.爬楼梯

8.二进制求和

关键字

二维数组一维化、行列号的映射关系、枚举法、滑动窗口、模拟竖式计算、字符串变量StringBuffer、双指针、动态规划初步、滚动数组


 

 1.重塑矩阵

难度:★   链接:力扣

425e9d2d32f54edd8b3e50f67a025879.png

 

解题思路:

        本题思路很简单,即将二维数组进行顺序行遍历,将数依次填入r行c列的数组中。若二者个数不相等则肯定无法重塑,直接返回原来数组;否则,进行二维数组的顺序行遍历,此时即将一个二维数组转化成了一个一维数组,下面一步最重要的就是如何确定具体的行列号坐标,通过观察我们发现:一个m行n列的二维数组中的一个数a[ i ][ j ] ,其行号i是由列号j除列数n得到的商所确定的,而列号j是由列号j对列数n取余所得到的, 即:  i=j/n   j=j%n   这个重要的映射关系对本题的填充数字很重要!

解题代码:

class Solution {
    public int[][] matrixReshape(int[][] mat, int r, int c) {
        int m=mat.length;
        int n=mat[0].length;
        int plan[][]=new int[r][c];
        //如果原矩阵与新矩阵的元素个数不同,那么肯定无法完成重塑
        if(m*n!=r*c){
            return mat;
        }
        //将二维数组视为经过行遍历而成的一个长度为m*n的一维数组
        for(int i=0;i<m*n;i++){
            //抓住主要映射关系,行数是由列来确定的
            plan[i/c][i%c]=mat[i/n][i%n];//填充
        }
        return plan;
    }
}

 


 

2.最长和谐子序列

难度:★  链接:力扣

2b2ed25ee8934692ab5ea7920efccc2d.png

 

解题思路:

枚举法,可以枚举数组中的每一个元素,对于当前元素x,它可以和x+1组成一个和谐子序列。所以问题可以转化为:我们在数组中寻找x和x+1的个数,就可以找到以x为最小值的和谐子序列的长度。这一思路是利用枚举法解题的关键所在。同时注意以下几点:

  • 实际处理时,我们可以将数组进行升序排序,只需要依次找到两个连续相同元素的子序列。检查这两个序列的元素之差是否为1,为1的话将其长度存在变量res中。比如:排序后的数组元素为1,2,2,2,3,3,3,3,3,4,5,5 此时找到的两个连续相同元素的子序列为:2 2 2 和 3 3 3 3 3 二者元素之差为1 ,符合条件,将其长度8存入res,进行下一轮的遍历,如果遇到更大的则更新
  • 利用类似“滑动窗口”的做法,设置两个遍历begin和end ,begin指向第一个连续相同元素的子序列的第一个元素,end指向相邻的第二个连续相同元素的子序列的末尾元素,如果满足二者之差为1,则当前的和谐子序列长度即为两个子序列的长度之和,等于end-begin+1

3686f5b9186e48bc8e8d3b1574137b70.png

 

解题代码:

class Solution {
    public int findLHS(int[] nums) {
        int begin=0;
        int res=0;
        Arrays.sort(nums);
        for(int end=0;end<nums.length;end++){
            while(nums[end]-nums[begin]>1){
                begin++;
            }
            if(nums[end]-nums[begin]==1){
                res=Math.max(res,end-begin+1);
            }
        }
        return res;
    }
}

 


 

3.验证回文串

难度:★ 链接:力扣

b12d2d7030554eccae5df25118c98c3a.png

 

解题思路:

方法一:筛选+判断,即:先将字符串中的字母与数字字符取出存入字符串变量StringBuffer中,最后将其进行翻转与原来进行字符串比较,相同则是回文串返回true ,否则不是回文串,返回false

解题代码:

class Solution {
    public boolean isPalindrome(String s) {
       int n=s.length();
       //设置字符串变量arr来存储字符串中的字母与数字字符
       StringBuffer arr=new StringBuffer();
       for(int i=0;i<n;i++){
           //调用API判断是否是字母或者数字字符
           char ch=s.charAt(i);
           if(Character.isLetterOrDigit(ch)){
               arr.append(Character.toLowerCase(ch));
           }
       }
       //将取出的字母数字字符串进行翻转后与原来进行字符串的比较
       //相同则是回文串返回true 否则返回false 
       StringBuffer res=new StringBuffer(arr).reverse();
       return res.toString().equals(arr.toString());
      
    }
}

 

方法二双指针法,设置两个指针left与right分别从已经取出字母数字字符的字符串的首尾同时向中间移动,步长相同,每移动一次判断各自对应的字符是否相等,不相等直接返回false ,否则继续移动,如果该字符串是回文串,则左右指针left与right终会相遇,返回true

解题代码:

class Solution {
    public boolean isPalindrome(String s) {
       int n=s.length();
       //设置字符串变量arr来存储字符串中的字母与数字字符
       StringBuffer arr=new StringBuffer();
       for(int i=0;i<n;i++){
           //调用API判断是否是字母或者数字字符
           char ch=s.charAt(i);
           if(Character.isLetterOrDigit(ch)){
               arr.append(Character.toLowerCase(ch));
           }
       }
       //双指针逐个字符进行判断
       String s2=arr.toString();
       int left=0,right=s2.length()-1;
       while(left<right){
           char ch1=s2.charAt(left);
           char ch2=s2.charAt(right);
           if(ch1!=ch2){
               return false;
           }else{
               left++;
               right--;
           }
       }
       return true;
    }
}

 


 

4.第三大的数

难度:★  链接:力扣

36578c27f26d46ba8a3bbab4f4e6a550.png

 

解题思路:

先将数组降序排列,然后从中找出第三个不重复的元素返回即可,我们用变量count来记录元素的种类,当达到3时返回此时的元素即为数组中第三大的元素 ;如果遍历结束仍未返回,则说明数组中元素不存在第三大的数字,返回最大的数nums[0](已经降序排列)

解题代码:

class Solution {
    public int thirdMax(int[] nums) {
        //先降序排序,然后再从前往后遍历计数不重复元素
        //选择排序
        int n=nums.length;
        for(int i=0;i<n-1;i++){
            int max=i;                                            
            for(int j=i+1;j<n;j++){
                if(nums[j]>nums[max]){
                    max=j;
                }
            }
            if(max!=i){
                int temp=nums[i];
                nums[i]=nums[max];
                nums[max]=temp;
            }
        }
        int count=1;
        for(int i=1;i<n;i++){
            if(nums[i]!=nums[i-1]&&++count==3){
                return nums[i];
            }
            
        }
        return nums[0];
    }
}

 


 

5.加一

难度:★  链接:力扣

 25d67b262d504aacaa2fe0c065649d34.png

 

解题思路:

一个数字加一,只需考虑末尾是否为9,即是否需要进位的情况;如果末尾为9则进一后原位置数值为0.如果末尾不是9则直接普通加一返回数组即可。注意这里所说的“末尾”是动态变化的,简单来说就是从后往前遍历数组,判断该位置的数字是否为9 ,最后别忽略了全部进位的情况,则要额外开辟一个空间给最高位即进位的1来使用。

解题代码:

class Solution {
    public int[] plusOne(int[] digits) {
        //题目本质就是在结尾加一,,分末尾数字是否为9进行判断
        int n=digits.length;
        for(int i=n-1;i>=0;i--){
            if(digits[i]==9){
                digits[i]=0;//满十进位后原位置的值就变为0
            }else{
                //末尾不是9的情况直接加一返回数组即可
                digits[i]+=1;
                return digits;
            }
        }
        //如果上面循环后还未返回,说明上面末尾为9后一次进位,之后前面的数依次全部进位了
        //所以需要额外开辟一个空间给最高位进位1使用
        digits=new int [n+1];
        digits[0]=1;
        return digits;
    }
}

 


 

6.汇总区间

难度:★  链接:力扣

c55ba3ffcde44f4b9bfbc85149924d30.png

 

解题思路:

 蛮力法,依次遍历找出连续区间,然后将此区间的左右端点加入字符串变量,然后将其加入动态数组中。注意点就是要判断该区间内连续元素的个数,如果只有一个,则不用输出区间直接输出单个值即可,否则要按照题目要求的格式 a->b的格式将其加入动态数组。

解题代码:

class Solution {
    public List<String> summaryRanges(int[] nums) {
        List<String>list=new ArrayList<>();
        int n=nums.length;
        int i=0;
        while(i<n){
            //遍历的起始位置,未来可能是区间的左端点位置
            int low=i++;//必须先加一,否则第一次遍历时会越界
            //判断相邻元素,即区间内的连续的值
            while(i<n&&nums[i]-nums[i-1]==1){
                i++;
            }
            //循环结束,此时i的位置即为不连续的第一个值
            int high=i-1;//连续区间的最后一个值的位置为i-1,即连续区间的右端点
            StringBuffer s=new StringBuffer(Integer.toString(nums[low]));
            //如果a!=b 则low与high不会相等,将箭头符号与右区间端点加入字符串变量中
            if(low<high){
                s.append("->");
                s.append(Integer.toString(nums[high]));
            }
            //如果a==b 则low 与 high会相等此时区间里就是一个单个值
            list.add(s.toString());
          
        }
         return list;
    }
}

 


 

7.爬楼梯

难度:★  链接:力扣

8c2061b05ae34fb29e68731ca81a4337.png

 

解题思路:

 动态规划法:我们用f(x)代表爬到第x级台阶的方案数,因为一次只能爬1级或者2级,所以最后一步可能是爬1级到达x级,或者爬2级到达x级,所以爬到x级的方案数是爬到x-1级台阶和爬到x-2级台阶的方案数之和,所以我们可以列出如下的式子:f(x) = f(x-1) + f(x-2) 。这就是动态规划的状态转移方程,即f(x)只能是由f(x-1)和f(x-2)转移过来 。

解题代码:

class Solution {
    public int climbStairs(int n) {
       int dp[]=new int[n+1];
       dp[0]=1;
       dp[1]=1;
       for(int i=2;i<=n;i++){
           dp[i]=dp[i-1]+dp[i-2];
       }
       return dp[n];
    }
}

注意到这里的时间复杂度与空间复杂度都为O(n),但是这里我们可以发现f(x)之和f(x-1)和f(x-2)有关,所以我们可以利用“滚动数组的思想”,把其空间复杂度优化为O(1),代码如下:

class Solution {
    public int climbStairs(int n) {
        int p = 0, q = 0, r = 1;
        for (int i = 1; i <= n; ++i) {
            p = q; 
            q = r; 
            r = p + q;
        }
        return r;
    }
}

滚动数组的思想是个基本思想,可以结合下面动图来理解

ed5027008506d236a349f96caf7c51fe.gif

另外贴一位扣友的总结,非常到位的总结

f1d5ac01e4c642018f650d6ed5a50fe6.png

 


 

8.二进制求和

难度:★   链接:力扣

2feec6f647bc4dceafcbf1c9cb035b28.png

 

解题思路:

本题类似于十进制加法,模拟其竖式计算,只不过进制不同,这是二进制加法,需要逢二进一,注意进位的值最后为1的情况

解题代码:

class Solution {
    public String addBinary(String a, String b) {
        //类似于模拟十进制的竖式计算,只不过这里是逢二进一
        StringBuffer res=new StringBuffer();
        int carry=0;//储存进位
        int m=a.length(),n=b.length();
        int i=m-1,j=n-1;
        while(i>=0||j>=0||carry!=0){
            //通过字符的ASCII码值与字符0相减得到整型值进行计算
            int x=i>=0?a.charAt(i)-'0':0;
            int y=j>=0?b.charAt(j)-'0':0;
            int sum=x+y+carry;
            res.append(sum%2);
            carry=sum/2;
            i--;
            j--;
        }
        //因为计算顺序与最终的二进制顺序相反,所以需要将计算结果翻转
        res.reverse();
        return res.toString();
    }
}

 

 

本专栏目前适合算法初学者学习,刷题范围主要是力扣的简单题偶尔夹杂中等题,语言主要使用Java,如果是水平很高的大佬可以略读本文,如果是还处在算法刷题初级阶段的朋友,可以点赞关注一下,我会一直更新下去的。

55c54c9ae7334e77b9c87771e002709e.png

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Aricl.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值