Leetcode算法题笔记(4)

1. 字符串数字相加

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
在这里插入图片描述

解法一

因为是字符串数字,如果字符串太长,可能直接导致int或long溢出。因此需要用竖式计算,用数组计算每位相乘得到的结果,可以先采用累加的方式,最后再统一进位计算。
例如:789 * 6 = 【42,48,54】,最后再统一进位,【4,7,3,4】

class Solution {
    public String multiply(String num1, String num2) {
        int len1 = num1.length();
        int len2 = num2.length();
        if(num1.equals("0") || num2.equals("0")) return "0";
        int[] result = new int[len1+len2];
        //每个槽中进行累加
        for(int i = len1-1; i >= 0; i-- ){
            for(int j = len2-1; j >= 0 ; j--){
                result[j+i+1] += (num1.charAt(i) - '0')*(num2.charAt(j) - '0');//注意j+i+1才是index
            }
        }
        //进行进位计算
        int count = 0;
        for(int i = len1+len2-1; i >= 0; i--){
            if(result[i] + count >= 10){
                result[i] += count;//进位
                count = result[i] / 10;
                result[i] = result[i] % 10;
            }else{
                result[i] += count;
                count = 0;
            }
        }
        //转换成字符串
        StringBuilder sb = new StringBuilder();
        boolean flag = false;
        for(int i = 0; i < len1+len2; i++){
            if( !flag && result[i] > 0){
                flag = true;
            }
            if(flag){
                sb.append(result[i]);
            }
        }
        return sb.toString();
        
    }
}

2. Z字形变换

将一个给定字符串 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);
在这里插入图片描述

解法一

维持一个StringBuilder数组,遍历字符串,首先从上到下依次放入字符到StringBuilder中,当字符串索引到底时(numRows)则开始反向,从下到上放置字符到StringBuilder的尾部,如此反复即可。

abcdefghi  3
a   e   i
b d f h
c   g
class Solution {
    public String convert(String s, int numRows) {
        if(numRows == 1) return s;
        StringBuilder[] sarr = new StringBuilder[numRows];
        for(int i = 0; i < numRows ; i++){
            sarr[i] = new StringBuilder();
        }
        int index = 0;
        boolean direct = false;
        int len = s.length();
        for(int i = 0; i < len; i++){
            sarr[index].append(s.charAt(i));
            if(!direct){//反向
                if(index == numRows - 1){
                    direct = true;
                    index = numRows - 2;
                }else{
                    index++;
                }
            }else{//反向
                if(index == 0){
                    direct = false;
                    index = 1;
                }else{
                    index--;
                }
            }
        }
        StringBuilder result = new StringBuilder();
        for(StringBuilder sb : sarr){
            result.append(sb);
        }
        return result.toString();
    }
}

3. 整数反转

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

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

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

解法一

利用取余和除法,依次取出最后一位数,然后与新数相加,每次新数都要乘以10。注意溢出问题。
在这里插入图片描述两边都除以10,即可得到新的判断式,且保证中间值不溢出32位数范围

123
3
310 + 2
(3
10+2)* 10 + 1 = 321

class Solution {
    public int reverse(int x) {
        int tmp = 0;
        int result = 0;
        while(x != 0){
            if(result < Integer.MIN_VALUE / 10 || result > Integer.MAX_VALUE/10) return 0;
            tmp = x % 10;
            x = x / 10;
            result = result*10 + tmp;
        }
        return result;
    }
}

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

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数。

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

空格:读入字符串并丢弃无用的前导空格(" ")
符号:检查下一个字符(假设还未到字符末尾)为 ‘-’ 还是 ‘+’。如果两者都不存在,则假定结果为正。
转换:通过跳过前置零来读取该整数,直到遇到非数字字符或到达字符串的结尾。如果没有读取数字,则结果为0。
舍入:如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被舍入为 −231 ,大于 231 − 1 的整数应该被舍入为 231 − 1 。
返回整数作为最终结果。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解题一

这题最主要是要读懂题意:遇到空格就跳过,遇到数字就开始计数,遇到-+就开始计数,如果空格-+排除后计数前遇到其他字符,则返回0;否则直接开始计数。计数时需要考虑之前的正负号。如果计数后遇到数字之外的其他字符,则直接返回结果。如果数字超过32位有符号整数的范围,则返回对应的边界值。

class Solution {
    public int myAtoi(String s) {
        if(s.equals("")) return 0;
        //首先去除空格
        int index = 0;
        while(index < s.length() && s.charAt(index) == ' ') index++;
        if(index == s.length()) return 0;
        int sign = 1;//正负标志位
        if(s.charAt(index) == '-'){
            sign = -1;
            index++;
        }else if(Character.isLetter(s.charAt(index))){
            return 0;
        }else if(s.charAt(index) == '+'){
            index++;
        }
        long result = 0;
        for(int i = index ; i < s.length(); i++){
            if(!Character.isDigit(s.charAt(i))){
                return (int)result;
            }
            result = result*10 + sign*(s.charAt(i) - '0');
            if(result > Integer.MAX_VALUE ) return Integer.MAX_VALUE;
            if(result < Integer.MIN_VALUE) return Integer.MIN_VALUE;
        }
        return (int)result;
    }
}

5. 数字 1 的个数

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
在这里插入图片描述

解法一

如果列举出所有小于等于n的数,求和1的数量时间复杂度太高。需要通过找规律,从低位遍历到高位,计算每一位上会出现1的次数:
下列规律本质上是将数字分为高位低位。计算某一位上出现1的次数时,其实是计算如果当前位为1,其他位有多少种变化。

  • 当当前位大于1时,高位可以从0~高位数字,共高位+1种变化,低位上由于当前位大于1,说明之前等于1的情况下,所有低位的可能情况都已经被包括,于是就是0-9 0-9 0-9,也就是10的n-1次方种变化。高位与低位变化相乘则为总共可能的情况
  • 当当前位等于1时,高位可以从0-高位-1,之所以减一是因为低位可能并不满足总是0-9 0-9 0-9。。。均可以(因为还没有成功进位,低位有一些情况还不能满足)。于是,比高位小一的数字则肯定能保证满足0-9 0-9 0-9。。。于是总共高位*10的低位-1次方。此外,还需要加上当高位数字不变时,剩下低位上剩余的数+1种变化。
  • 当高位小于1,说明当前高位不变的情况下,肯定不存在当前位为1的情况。直接高位*10的低位-1次方。
    在这里插入图片描述
class Solution {
    public int countDigitOne(int n) {
        int index = 1;
        int top = n;
        int low = 0;
        int cur = 1;
        int result = 0;
        while(top > 0){
            top = (int) (n / Math.pow(10,index));
            low = (int) (n % Math.pow(10,index-1));
            cur = (int) (n / Math.pow(10,index-1) % 10);
            if(cur > 1){
                result += (top + 1)*Math.pow(10,index-1);
            }else if( cur == 1){
                result += (top)*Math.pow(10,index-1) + low + 1;
            }else{
                result += (top)*Math.pow(10,index-1);   
            }
            index++;
        }
        return result;
    }
}

6. 两数相除

给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。

整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。

返回被除数 dividend 除以除数 divisor 得到的 商 。

注意:假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−2^31, 2^31 − 1] 。本题中,如果商 严格大于 2^31 − 1 ,则返回 2^31 − 1 ;如果商 严格小于 -2^31 ,则返回 -2^31 。

解法一

类似于之前用快速幂求幂次方,采用二分法,这里是加法,2+2,4+4,8+8,16+16…,这样可以避免除数一个一个累加导致超时。递归地方式如下:需要注意,-231-1如果除以-1会变成231,32位数存不下,需要直接返回int最大值。被除数为0,直接返回0即可。
上述对半相加的方式可能会导致溢出,因此可以将判断x+x是否大于目标值改为,目标值减x是否小于x。此外,有可能

class Solution {
    public int divide(int dividend, int divisor) {
        if(dividend == Integer.MIN_VALUE && divisor == 1) return Integer.MIN_VALUE;
        if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE;
        // 考虑除数为最小值的情况
        if (divisor == Integer.MIN_VALUE) {
            return dividend == Integer.MIN_VALUE ? 1 : 0;
        }
        if(dividend == 0) return 0;
        boolean flag = false;
        //被除数和除数全部转换为负数进行计算,如果全部转换为正数可能正数最大值存不下
        //之所有进行统一符号转换是为了方便后面采用同样的计算方法,两个数同号只用考虑加即可
        if(dividend > 0){
            dividend = -dividend;
            flag = !flag;
        }
        if(divisor > 0){
            divisor = -divisor;
            flag = !flag;
        }
        return flag ? -newdivide(dividend,divisor) : newdivide(dividend,divisor);
    }
    public int newdivide(int dividend,int divisor){
        if(dividend > divisor) return 0;//重要,不能只凭dividend大于0判断就返回0,因为可能存在dividen和divisor都是负数,但dividend已经不够除了,下面count就又会多加一个1.
        int tmp = divisor;
        int count = 1;
        while(divisor >= dividend - divisor){
            count += count;//成倍数增加
            divisor += divisor;
        }
        return count + newdivide(dividend - divisor,tmp);
    }
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值