算法通关村第十三关——数字与数学基础问题(青铜)
1 数字统计专题
1.1 数组元素积的符号
方法一:符号乘积
根据题意的第一想法就是直接相乘再判断符号位,但是这样很容易溢出,所以我们再看可以发现,只需要知道符号位即可
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 阶乘尾数
这道题的思想。。其实跟数学有关,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 整数反转
方法一:逐个判断
我们只要能拿到这个整数的 末尾数字 就可以了。
以12345为例,先拿到5,再拿到4,之后是3,2,1,我们按这样的顺序就可以反向拼接处一个数字了,也就能达到 反转 的效果。
怎么拿末尾数字呢?好办,用取模运算就可以了。
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 字符串转换整数
没啥好说的,主要是需要处理的条件比较多,一个个理顺,解决即可:
- 去除前导空格
- 判断是否出现符号字符,并记录正负号
- 处理数字字符,直到遇到非数字字符或者遍历结束
- 处理数字时,注意越界的条件
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 回文数
思路:
映入脑海的第一个想法是将数字转换为字符串,并检查字符串是否为回文。但是,这需要额外的非常量空间来创建问题描述中所不允许的字符串。
第二个想法是将数字本身反转,然后将反转后的数字与原始数字进行比较,如果它们是相同的,那么这个数字就是回文。 但是,如果反转后的数字大于 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,
由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
- 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
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
}
}