C/C++教程

算法刷题(LeetCode 6-10)

本文主要是介绍算法刷题(LeetCode 6-10),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

    • [6. Z 字形变换 9/2](https://leetcode-cn.com/problems/zigzag-conversion/)
    • [7. 整数反转 9/3](https://leetcode-cn.com/problems/reverse-integer/)
    • [8. 字符串转换整数 (atoi) 9/4](https://leetcode-cn.com/problems/string-to-integer-atoi/)
    • [9. 回文数 9/5](https://leetcode-cn.com/problems/palindrome-number/)
    • [10. 正则表达式匹配 9/6](https://leetcode-cn.com/problems/regular-expression-matching/)

6. Z 字形变换 9/2

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

在这里插入图片描述
在这里插入图片描述


解析:

找出按 Z 形排列后字符的规律,然后直接保存起来。

在这里插入图片描述
可以看到,图形其实是有周期的,0,1,2 … 7 总共 8 个,然后就又开始重复相同的路径。周期(cycleLen)的计算就是:cycleLen = 2 × 行数(numRows) - 2 = 2 × 5 - 2 = 8 个。

我们发现第 0 行和最后一行一个周期内只有一个字符,所以第一个字符下标是 0 ,第二个字符下标是 0 + cycleLen = 8,第三个字符下标是 8 + cycleLen = 16 。

中间其他行一个周期内都是两个字符。

第 1 个字符和第 0 行的规律是一样的。

第 2 个字符其实就是下一个周期的第 0 行的下标减去当前行。什么意思呢?

我们求一下第 1 行第 1 个周期内的第 2 个字符,下一个周期的第 0 行的下标是 8 ,减去当前行 1 ,就是 7 了。

我们求一下第 1 行第 2 个而周期内的第 2 个字符,下一个周期的第 0 行的下标是 16 ,减去当前行 1 ,就是 15 了。

我们求一下第 2 行第 1 个周期内的第 2 个字符,下一个周期的第 0 行的下标是 8 ,减去当前行 2 ,就是 6 了。

当然期间一定要保证下标小于 字符串长度(n) ,防止越界。


Java实现:

class Solution {
    public String convert(String s, int numRows) {
        //如果只有一行,则直接返回字符串
        if(numRows == 1){
            return s;
        }

        //字符串长度
        int len = s.length();
        //用来存储输出字符串
        StringBuilder ret = new StringBuilder();
        //计算每个周期字符个数
        int cycleLen = 2 * numRows - 2;
        
        //遍历行
        for(int i=0; i<numRows; i++){
            //遍历周期数,每次加一个周期
            for(int j=0; j+i<len; j+=cycleLen){
                //添加的是每个周期的第一列
                ret.append(s.charAt(j + i));
                //添加的是 第i行 第j个 周期的第二个字符;即 除去第0行和最后一行
                if(i != 0 && i != numRows - 1 && j+cycleLen-i < len){
                    ret.append(s.charAt(j + cycleLen - i));
                }
            }
        }
        return ret.toString();
    }
}

在这里插入图片描述
时间复杂度:O(n),虽然是两层循环,但第二次循环每次加的是 cycleLen ,无非是把每个字符遍历了 1 次,所以两层循环内执行的次数肯定是字符串的长度。

空间复杂度:O(n),保存字符串。

7. 整数反转 9/3

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

在这里插入图片描述
在这里插入图片描述


解法一:

将整数转为字符串,字符串反转后再转为整数即可,需要主要负数的情况。

class Solution {
    public int reverse(int x) {
        String sx = Integer.toString(x);  //可能带负号
        String s = sx; //存储新的字符串
        int flag = 1; //默认正数
        //如果x为负数,则需乘 -1,设置flag为-1
        if(x<0){
            flag = -1; //负数
            s = sx.substring(1); //去除负号
        }
        try{
            //字符串反转后转整数
            x = Integer.valueOf((new StringBuilder(s)).reverse().toString()) * flag;
            return x;
        }catch (Exception e){
            return 0;
        }
    }
}

在这里插入图片描述


解法二:

将整数不断取余得到个位数,然后再除十去掉个位数;然后用一个变量来存放结果。

class Solution {
    public int reverse(int x) {
        long rev = 0; //存值变量
        while(x != 0){
            int pop = x % 10; //个位数
            x /= 10; //取整, 去除个位数
            rev = rev * 10 + pop;
        }
        //如果超过范围 就返回0
        if(rev > Integer.MAX_VALUE || rev < Integer.MIN_VALUE) return 0;
        return (int)rev;
    }
}

在这里插入图片描述
时间复杂度:循环多少次呢?数字有多少位,就循环多少次,也就是 log10(x)+1log_{10}(x) + 1log​10​​(x)+1 次,所以时间复杂度是 O(log(x))。

空间复杂度:O(1)。

8. 字符串转换整数 (atoi) 9/4

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  • 读入字符串并丢弃无用的前导空格
  • 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。如果两者都不存在,则假定结果为正。
  • 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  • 将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0。必要时更改符号(从步骤 2 开始)。
  • 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1],需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1的整数应该被固定为 231 − 1 。
  • 返回整数作为最终结果。

注意:

  • 本题中的空白字符只包括空格字符 ’ ’ 。
  • 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


解析:

主要是考虑好字符的处理方式。


Java实现:

class Solution {
    public int myAtoi(String s) {
        int i = 0;
        int len = s.length(); //字符串长度
        int sign = 1; //正数为1,负数为-1
        int res = 0; //存储整数
        while (i < len && s.charAt(i) == ' ') { //如果字符串前导位置为空格,循环到有数据的那一个位置
            i++;
        }
        int start = i;  //记录一下当前之后所有数据开始的位置
        for (; i < len; i++) {
            int c = s.charAt(i); //当前字符
            if (i == start && c == '+') {   //判断是否是+,并且+要在初始位置
                sign = 1;
            } else if (i == start && c == '-') {    //判断是-
                sign = -1;
            } else if (Character.isDigit(c)) {  //判断是数字
                int num = c - '0';
                //如果是数字,其他不用考虑,只需要考虑两种超限的情况
                if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && num > Integer.MAX_VALUE % 10)) {
                    return Integer.MAX_VALUE;
                } else if (res < Integer.MIN_VALUE / 10 || (res ==Integer.MIN_VALUE / 10 && -num < Integer.MIN_VALUE % 10)) {
                    return  Integer.MIN_VALUE;
                }
                //计算整数值
                res = res * 10 + sign * num;
            } else {    //如果有一次循环既不是数字,又不是'+'和'-',那么立即退出循环,并返回当前res中已经储存的值
                break;
            }
        }
        return res;
    }
}

在这里插入图片描述

9. 回文数 9/5

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
在这里插入图片描述
在这里插入图片描述


解法一:

将整数转为字符串,使用双指针解决。

class Solution {
    public boolean isPalindrome(int x) {
        String s = String.valueOf(x); //将整数转为字符串
        int left = 0; //左指针
        int right = s.length() - 1; //右指针
        while(left < right){ 
            if(s.charAt(left) != s.charAt(right)){ //如果左边字符不等于右边字符,不是回文
                return false;
            }
            left++; //移动左指针
            right--; //移动右指针
        }
        return true;
    }
}

在这里插入图片描述


解法二:

将右半部分倒置然后和左半部比较就可以了。比如 1221 ,把 21 转置和 12 比较就行了。

class Solution {
    public boolean isPalindrome(int x) {
        if(x<0){
            return false;
        }
        int n = (int) (Math.log(x) / Math.log(10) + 1); //总位数 
        int rev = 0; //存放右边反转的值
        int pop = 0; //存放个位数值
        //倒置右半部分
        for(int i=0; i<n/2; i++){
            pop = x % 10; //个位数
            rev = rev * 10 + pop; //计算值
            x /= 10; //取整,去除已计算的个位数
        }
        //如果是整数的总位数是偶数
        if(n % 2 == 0 && x == rev){
            return true;
        }
        //如果是整数的总位数是奇数,就将x去除一位再与右边比较
        if(n % 2 != 0 && x/10 == rev){
            return true;
        }
        return false;
    }
}

在这里插入图片描述
时间复杂度:循环 x 的总位数的一半次,所以时间复杂度依旧是 O(log(x))。

空间复杂度:O(1),常数个变量。

10. 正则表达式匹配 9/6

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

  • ‘.’ 匹配任意单个字符
  • ‘*’ 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
在这里插入图片描述

在这里插入图片描述


解法一:

递归

class Solution {
    public boolean isMatch(String s, String p) {
        //判断当p为空时,如果s也为空,就返回true,否则为false
        if(p.isEmpty()) return s.isEmpty();

        //当s不为空,并且 p与s的第一个字符相等 或 p为'.'时,为true
        boolean first_match = (!s.isEmpty() && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.'));

        //当p长度大于等于2时,才考虑'*'
        if(p.length() >= 2 && p.charAt(1) == '*'){
            //两种情况
            //1. p直接跳过两个字符:表示*前面的字符出现0次
            //2. p不变,例如 s=aa,p=a* 第一个a匹配,然后s的第二个a接着和 p的第一个a匹配 :表示 * 用前一个字符替代。
            return (isMatch(s,p.substring(2))) || (first_match && isMatch(s.substring(1),p));
        } else {
        //递归
        return first_match && isMatch(s.substring(1),p.substring(1)); 
        }


    }
}

在这里插入图片描述


解法二:

动态规划

class Solution {
    public boolean isMatch(String text, String pattern) {
        // 多一维的空间,因为求 dp[len - 1][j] 的时候需要知道 dp[len][j] 的情况,
        // 多一维的话,就可以把 对 dp[len - 1][j] 也写进循环了
        boolean[][] dp = new boolean[2][pattern.length() + 1]; 
        dp[text.length()%2][pattern.length()] = true;

        // 从 len 开始减少
        for (int i = text.length(); i >= 0; i--) {
            for (int j = pattern.length(); j >= 0; j--) {
                if(i==text.length()&&j==pattern.length()) continue;
                boolean first_match = (i < text.length() && j < pattern.length()
                        && (pattern.charAt(j) == text.charAt(i) || pattern.charAt(j) == '.'));
                if (j + 1 < pattern.length() && pattern.charAt(j + 1) == '*') {
                    dp[i%2][j] = dp[i%2][j + 2] || first_match && dp[(i + 1)%2][j];
                } else {
                    dp[i%2][j] = first_match && dp[(i + 1)%2][j + 1];
                }
            }
        }
        return dp[0][0];
    }
}

在这里插入图片描述

这篇关于算法刷题(LeetCode 6-10)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!