算法通关村第十三关——数字与数学基础问题(青铜)

1 数字统计专题

1.1 数组元素积的符号

leetcode 1822. 数组元素积的符号

方法一:符号乘积

根据题意的第一想法就是直接相乘再判断符号位,但是这样很容易溢出,所以我们再看可以发现,只需要知道符号位即可

class Solution {
    public int arraySign(int[] nums) {
        int res = 1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {
                return 0;
            }
            int num = nums[i] >> 31 == 0 ? 1 : -1;
            res *= num;
        }
        return res > 0 ? 1 : -1;
    }
}
方法二:符号个数

我们只需统计负数的数量即可确定整个数组的乘积的符号。

  • 如果负数的数量是偶数,说明整个数组的乘积为正数;

  • 如果负数的数量是奇数,则整个数组的乘积为负数。

class Solution {
    public int arraySign(int[] nums) {
        int countNegative = 1;
        
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {
                return 0;
            } else if (nums[i] < 0) {
                countNegative = -countNegative;
            }
        }
        
        return countNegative;
    }
}

这里用了个小技巧countNegative = -countNegative;

这样就不需要统计个数了,直接得到对应的数

1.2 阶乘尾数

leetcode 面试题 16.05. 阶乘尾数

这道题的思想。。其实跟数学有关,md

由于每一个零都是由2和5相乘得到的,所以要计算末尾的零的个数,只需要计算n!中有多少个因子5即可

class Solution {
    public int trailingZeroes(int n) {
        int count = 0;
        while (n > 0) {
            n /= 5;
            count += n;
        }
        return count;
    }
}

2 溢出问题

2.1 整数反转

leetcode 7. 整数反转

方法一:逐个判断

我们只要能拿到这个整数的 末尾数字 就可以了。
以12345为例,先拿到5,再拿到4,之后是3,2,1,我们按这样的顺序就可以反向拼接处一个数字了,也就能达到 反转 的效果。
怎么拿末尾数字呢?好办,用取模运算就可以了。

1.jpg

1、将12345 % 10 得到5,之后将12345 / 10
2、将1234 % 10 得到4,再将1234 / 10
3、将123 % 10 得到3,再将123 / 10
4、将12 % 10 得到2,再将12 / 10
5、将1 % 10 得到1,再将1 / 10

class Solution {
    public int reverse(int x) {
        int res = 0;
        int maxNum = Integer.MAX_VALUE / 10;
        int minNum = maxNum * -1;
        while(x != 0){
            int endNum = x % 10;
            if(res > maxNum || (res == maxNum && endNum > 7) ){
                return 0;
            }
            if(res < minNum || (res == minNum && endNum < -8) ){
                return 0;
            }
            res = res * 10 + endNum;
            x /= 10;
        }
        return res;
    }
}
方法二:比较上次

上面那个算法还可以进行优化,主要的思想是:
反转结果与上一次的结果不相等,说明反转后的整数越界

为啥呢??

  • 当反转前的结果 res 乘以 10 加上当前位数得到反转后的结果 newRes,如果 newRes 超出了 int 类型的取值范围,则计算 (newRes - endNum) / 10 的结果会与 res 不相等。

  • 这是因为在溢出的情况下,(newRes - endNum) / 10 的结果已经不再等于 res,它会变成一个截断后的值。

  • 通过判断新的结果 newRes 和上一次的结果 res 是否相等,我们可以检测到整数溢出的情况,并返回0作为越界的标志。

class Solution {
    public int reverse(int x) {
        int res = 0;
        while (x != 0) {
            int endNum = x % 10;
            int newRes = res * 10 + endNum;
            if ((newRes - endNum) / 10 != res) {
                return 0;
            }
            res = newRes;
            x /= 10;
        }
        return res;
    }
}

2.2 字符串转换整数

leetcode 8. 字符串转换整数 (atoi)

没啥好说的,主要是需要处理的条件比较多,一个个理顺,解决即可:

  1. 去除前导空格
  2. 判断是否出现符号字符,并记录正负号
  3. 处理数字字符,直到遇到非数字字符或者遍历结束
  4. 处理数字时,注意越界的条件
lass Solution {
    public int myAtoi(String s) {
        int sign = 1; // 符号位,默认为正数
        int ans = 0; // 结果变量
        int index = 0; // 字符串遍历的索引
        char[] array = s.toCharArray(); // 将输入字符串转换为字符数组
        
        // 去除前导空格
        while (index < array.length && array[index] == ' ') {
            index++;
        }
        
        // 判断是否出现符号字符,并记录正负号
        if (index < array.length && (array[index] == '-' || array[index] == '+')) {
            sign = array[index++] == '-' ? -1 : 1;
        }
        
        // 处理数字字符,直到遇到非数字字符或者遍历结束
        while (index < array.length && array[index] <= '9' && array[index] >= '0') {
            int digit = array[index++] - '0'; // 当前数字字符对应的数值
            
            // 提前判断乘以10以后是否会越界
            // 如果越界,则根据正负号返回Integer.MAX_VALUE或Integer.MIN_VALUE
            if (ans > (Integer.MAX_VALUE - digit) / 10) {
                return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            }
            
            // 更新结果变量,将当前数字字符转换为数值并累加到结果中
            ans = ans * 10 + digit;
        }
        
        return ans * sign; // 返回带上符号位的结果
    }
}

2.3 回文数

leetcode 9. 回文数

思路:

映入脑海的第一个想法是将数字转换为字符串,并检查字符串是否为回文。但是,这需要额外的非常量空间来创建问题描述中所不允许的字符串。

第二个想法是将数字本身反转,然后将反转后的数字与原始数字进行比较,如果它们是相同的,那么这个数字就是回文。 但是,如果反转后的数字大于 int.MAX\text{int.MAX}int.MAX,我们将遇到整数溢出问题。

按照第二个想法,为了避免数字反转可能导致的溢出问题,为什么不考虑只反转 int\text{int}int 数字的一半?毕竟,如果该数字是回文,其后半部分反转后应该与原始数字的前半部分相同。

例如,输入 1221,我们可以将数字 “1221” 的后半部分从 “21” 反转为 “12”,并将其与前半部分 “12” 进行比较,因为二者相同,我们得知数字 1221 是回文。

算法

  • 末尾数:x % 10,剩余数:x/10,然后一直遍历即可拿到每个数
  • 反转的数:x = x * 10 + x % 10,这样就相当于反转了
  • 当原始数字小于或等于反转后的数字时,就意味着我们已经处理了一半位数的数字了。
    • 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
      例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
      由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
class Solution {
    public boolean isPalindrome(int x) {
        if (x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }
        int revertedNumber = 0;
        while (x > revertedNumber) {
            revertedNumber = revertedNumber * 10 + x % 10;
            x /= 10;
        }
        return x == revertedNumber || x == revertedNumber / 10;
    }
}

3 进制专制

3.1 七进制数

首先理解10进制怎么转换为7进制

100 / 7 = 14 余 2

14 / 7 = 2 余 0

2 / 7 = 0余 2

根据以上的例子,100的7进制为202

得出以下代码

class Solution {
    public String convertToBase7(int num) {
        StringBuilder res = new StringBuilder();
        if (num == 0){	
            return "0";
        }
        boolean sign = num < 0;
        if(sign){
            num *= -1;
        }
        do{
            res.append(String.valueOf(num % 7) );
            num /= 7;
        }while(num > 0);
        if(sign){
            res.append("-");
        }
        return res.reverse().toString();
    }
}

3.2 进制转换

把上面的方法就可以转换为得到不同进制的数:

public class Solution {
    public String convertToBaseM(int n, int m) {
        if (n == 0) {
            return "0";
        }
        
        StringBuilder res = new StringBuilder();
        boolean sign = false;
        if (n < 0) {
            sign = true;
            n = -n;
        }

        char[] digits = new char[m]; // 创建字符数组用于存储对应进制的字符表示
        for (int i = 0; i < m; i++) {
            digits[i] = Character.forDigit(i, m);
        }

        while (n > 0) {
            res.insert(0, digits[n % m]); // 直接通过数组索引获取对应的字符
            n /= m; // 更新n为商
        }

        if (sign) {
            res.insert(0, "-");
        }

        return res.toString();
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.convertToBaseM(100, 7)); // 输出:202
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值