这里写目录标题
数学的位操作
1:整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
题解: 用取模运算向拼接处一个数字了,也就能达到 反转 的效果。 循环的判断条件是x>0但这样不对,因为忽略了 负数 循环的判断条件应该是while(x!=0),无论正数还是负数,按照上面不断的/10这样的操作,最后都会变成0,所以判断终止条件就是!=0
有了取模和除法操作,对于像12300这样的数字,也可以完美的解决掉了。假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]。
也就是说我们不能用long存储最终结果,而且有些数字可能是合法范围内的数字,但是反转过来就超过范围了。假设有1147483649这个数字,它是小于最大的32位整数2147483647的,但是将这个数字反转过来后就变成了9463847411,这就比最大的32位整数还要大了,这样的数字是没法存到int里面的,所以肯定要返回0(溢出了)。
class Solution {
public int reverse(int x) {
int res = 0;
while(x!=0) {
//每次取末尾数字
int tmp = x%10;
//判断是否 大于 最大32位整数
if (res>214748364 || (res==214748364 && tmp>7)) {
return 0;
}
//判断是否 小于 最小32位整数
if (res<-214748364 || (res==-214748364 && tmp<-8)) {
return 0;
}
res = res*10 + tmp;
x /= 10;
}
return res;
}
}
2.回文数
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
例如,121 是回文,而 123 不是。
public static boolean isPalindrome(int x) {
int y=0;
int x1=x;
while (x>0){
y =y*10+(x%10);
x/=10;
}
if (y==x1)return true;
return false;
}
力扣官方解法
public boolean isPalindrome(int x) {
// 特殊情况:
// 如上所述,当 x < 0 时,x 不是回文数。
// 同样地,如果数字的最后一位是 0,为了使该数字为回文,
// 则其第一位数字也应该是 0
// 只有 0 满足这一属性
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revertedNumber = 0;
while (x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
// 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
// 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
// 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
return x == revertedNumber || x == revertedNumber / 10;
}
479. 最大回文数乘积
class Solution {
public int largestPalindrome(int n) {
if (n == 1) {
return 9;
}
int upper = (int) Math.pow(10, n) - 1;
int ans = 0;
for (int left = upper; ans == 0; --left) { // 枚举回文数的左半部分
long p = left;
for (int x = left; x > 0; x /= 10) {
p = p * 10 + x % 10; // 翻转左半部分到其自身末尾,构造回文数 p
}
for (long x = upper; x * x >= p; --x) {
if (p % x == 0) { // x 是 p 的因子
ans = (int) (p % 1337);
break;
}
}
}
return ans;
}
}
第二种解法- 直接打表
打表常见的用法有如下几种:
1、在程序中一次性计算出所有需要用到的结果,之后的查询直接取这些结果
这个是最常用到的用法,例如在一个需要查询大量Fibonacci数F(n)的问题中,显然每次从头开始计算是非常耗时的,对Q次查询会产生O(nQ)的时间复杂度;而如果进行预处理,即把所有Fibonacci数预先计算并存在数组中,那么每次查询就只需O(1)的时间复杂度,对Q次查询就值需要O(n+Q)的时间复杂度(其中O(n)是预处理的时间)。
2、在程序B中分一次或多次计算出所有需要用到的结果,手工把结果写在程序A的数组中,然后在程序A中就可以直接使用这些结果
这种用法一般是当程序的一部分过程小号的时间过多,或是没有想到好的算法,因此在另一个程序中使用暴力算法算出结果,这样就能直接在源程序中使用这些结果。例如对n皇后问题来说,如果使用的算法不够好,就容易超时,而可以在本地用程序计算付出对所有n来说n皇后问题的方案数,然后把算出的结果直接卸载数组中,就可以根据题目输入的n来直接输出结果。
3、对一些感觉不会做的题目,先用暴力程序计算小范围数据的结果,然后找规律,或许就能发现一些“蛛丝马迹”
{9,987,123,597,677,1218,877,475}
231.2 的幂
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true;否则,返回 false 。
public boolean isPowerOfTwo(int n) {
if(n == 0)return false;
if(n < 0)return false;
return ( n & (n -1)) == 0;
}
342.4的幂
给定一个整数,写一个函数来判断它是否是 4 的幂次方。如果是,返回 true ;否则,返回 false 。
public boolean isPowerOfFour(int n) {
return n>0&&( n & (n -1)) == 0 && (n & 0x55555555) != 0;
}
326.3 的幂
给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。
public boolean isPowerOfThree(int n) {
while (n != 0 && n % 3 == 0) {
n /= 3;
}
return n == 1;
}
504 . 7进制
给定一个整数 num,将其转化为 7 进制,并以字符串形式输出。
public String convertToBase7(int num) {
//给定一个整数 num,将其转化为 7 进制,并以字符串形式输出。
//用除留取余数法
if (num == 0){
return "0";
}
int n =num;
Stack<Integer> stack = new Stack<>();
while (n != 0){
stack.add(Math.abs(n%7));
n=n/7;
}
StringBuilder stringBuilder = new StringBuilder();
while (!stack.isEmpty()){
stringBuilder.append(stack.pop());
}
if (num < 0)return "-"+stringBuilder.toString();
return stringBuilder.toString();
}
263 . 丑数
丑数 就是只包含质因数 2、3 和 5 的正整数。
给你一个整数 n ,请你判断 n 是否为 丑数 。如果是,返回 true ;否则,返回 false 。
public boolean isUgly(int n) {
//丑数 就是只包含质因数 2、3 和 5 的正整数
if (n <= 0) {
return false;
}
int[] factors = {2, 3, 5};
for (int factor : factors) {
while (n % factor == 0) {
n /= factor;
}
}
return n == 1;
}
190 .颠倒二进制位
颠倒给定的 32 位无符号整数的二进制位。
将 n 视作一个长为 32 的二进制串,从低位往高位枚举 n 的每一位,将其倒序添加到翻转结果 rev 中。
代码实现中,每枚举一位就将n 右移一位,这样当前 n 的最低位就是我们要枚举的比特位。当 n 为 0 时即可结束循环。
需要注意的是,在某些语言如 Java中,没有无符号整数类型,因此对 n 的右移操作应使用逻辑右移。
public int reverseBits(int n) {
//颠倒给定的 32 位无符号整数的二进制位。
int rev = 0;
for (int i = 0; i < 32 && n != 0; ++i) {
rev |= (n & 1) << (31 - i);
n >>>= 1;
}
return rev;
}
191 . 位1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
public int hammingWeight(int n) {
// 二进制1的个数
int ret = 0;
while (n != 0){
n=n&n-1;
ret++;
}
return ret;
}
476 .补数
对整数的二进制表示取反(0 变 1 ,1 变 0)后,再转换为十进制表示,可以得到这个整数的补数。
解法: 找到最高位的1,然后按位取反就行
public int findComplement(int num) {
int s = -1;
// 找到最高位的1
for (int i = 31; i >= 0; i--) {
if (((num >> i) & 1) != 0) {
s = i;
break;
}
}
// 然后按位取反
int ans = 0;
for (int i = 0; i < s; i++) {
if (((num >> i) & 1) == 0) ans |= (1 << i);
}
return ans;
}
461 .汉明距离
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
,记 s=x⊕y我们可以不断地检查 s 的最低位,如果最低位为 1,那么令计数器加一,然后我们令 s 整体右移一位,这样 s 的最低位将被舍去,原本的次低位就变成了新的最低位。我们重复这个过程直到 s=0为止。这样计数器中就累计了 s 的二进制表示中 1 的数量。
public int hammingDistance(int x, int y) {
int s = x ^ y, ret = 0;
while (s != 0) {
ret += s & 1;
s >>= 1;
}
return ret;
}
693.交替位二进制数
给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同
解法: 我们用对 2 取模再除以 2 的方法,依次求出输入的二进制表示的每一位,并与前一位进行比较。如果相同,则不符合条件;如果每次比较都不相同,则符合条件
public static boolean hasAlternatingBits(int n) {
int prev = 2;
while (n != 0) {
int cur = n % 2;
if (cur == prev) {
return false;
}
prev = cur;
n /= 2;
}
return true;
}
172.阶乘后的零
给定一个整数 n ,返回 n! 结果中尾随零的数量。
解法: 判断阶层中5的个数
public int trailingZeroes(int n) {
int ans = 0;
for (int i = 5; i <= n; i += 5) {
for (int x = i; x % 5 == 0; x /= 5) {
++ans;
}
}
return ans;
}
258.各位相加
给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。返回这个结果。
public int addDigits(int num) {
while (num >= 10) {
int sum = 0;
while (num > 0) {
sum += num % 10;
num /= 10;
}
num = sum;
}
return num;
}
405.数字转换为十六进制数
对于负整数,我们通常使用 补码运算 方法。
public String toHex(int _num) {
if (_num == 0) return "0";
long num = _num;
StringBuilder sb = new StringBuilder();
if(num < 0) num = (long)(Math.pow(2, 32) + num);
while (num != 0) {
long u = num % 16;
char c = (char)(u + '0');
if (u >= 10) c = (char)(u - 10 + 'a');
sb.append(c);
num /= 16;
}
return sb.reverse().toString();
}
171.Excel 表列序号
public String toHex(int _num) {
if (_num == 0) return "0";
long num = _num;
StringBuilder sb = new StringBuilder();
if(num < 0) num = (long)(Math.pow(2, 32) + num);
while (num != 0) {
long u = num % 16;
char c = (char)(u + '0');
if (u >= 10) c = (char)(u - 10 + 'a');
sb.append(c);
num /= 16;
}
return sb.reverse().toString();
}
168. Excel表列名称
public String convertToTitle(int columnNumber) {
StringBuffer sb = new StringBuffer();
while (columnNumber > 0) {
int a0 = (columnNumber - 1) % 26 + 1;
sb.append((char)(a0 - 1 + 'A'));
columnNumber = (columnNumber - a0) / 26;
}
return sb.reverse().toString();
}
670. 最大交换
给定一个非负整数,你至多可以交换一次数字中的任意两位。返回你能得到的最大值。
核心思路:将最靠后的最大的数,与最靠前的小于它的数交换(若存在),
public int maximumSwap(int num) {
char[] ch = String.valueOf(num).toCharArray();
int len = ch.length;
int maxIndex = len - 1;//定义候选数的位置
int left = 0,right = 0;//定义两个需要交换的数的位置
for (int i = len - 1;i >= 0;i--) {
if (ch[i] > ch[maxIndex]) maxIndex = i;//若候选数前出现了比它大的数,那么更新候选数。
else if (ch[i] < ch[maxIndex]) {
left = i;
right = maxIndex;//只有当候选数前存在比它小的数,才需要交换位置
}
}
swap(ch,left,right);
return Integer.parseInt(new String(ch));
}
public void swap(char[] arr,int i,int j) {
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
233.数字 1 的个数
给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
class Solution {
public int countDigitOne(int n) {
int digit = 1, res = 0;
int high = n / 10, cur = n % 10, low = 0;
while(high != 0 || cur != 0) {
if(cur == 0) res += high * digit;
else if(cur == 1) res += high * digit + low + 1;
else res += (high + 1) * digit;
low += cur * digit;
cur = high % 10;
high /= 10;
digit *= 10;
}
return res;
}
}
357.统计各位数字都不同的数字个数
n=0,数字有{0}1个。
n=1,数字有{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}10个。
n=2,数字包括两部分之和,一部分为n=1的所有10个答案,另一部分为长度为2的新增数字。长度为2的新增数字可以在n=1的所有9个数字基础上进行拼接(0不能算)。例如:
从n=1的数字列表{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}中随便取出一个除0以外的数字(因为0不能作为起始数字!),我们取2好了。通过在2的尾巴处拼接一位数字可以得到新的合法数字有:
{20, 21,23,24,25,26,27,28,29},
可以看到,除了不能在尾巴处拼接一个2(两个连续的2就非法了!),0-9种一共有9个数字可以拿拼接在尾巴处。新增答案为9个。同理,对于n=1数字列表{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}中的其他任意非0数也可以进行拼接操作,一共可以新增99个答案。
最终,n=2的合法数字,n=1时的答案 + 长度为2的数字个数(99个)= 10 + 81 = 91。
n=3时同理,只不过此时可以用拼接的数字减少为了8个,此时答案为10 + 9 * 9 + 9 * 9 * 8 = 739。
通过归纳不难得到,假设 dp[i] 即 n = i时的答案,则动态转移方程为:
dp[i] = dp[i-1] + (dp[i-1] - dp[i-2])*(10-(i-1))
public int countNumbersWithUniqueDigits(int n) {
if (n == 0) {
return 1;
}
if (n == 1) {
return 10;
}
int res = 10, cur = 9;
for (int i = 0; i < n - 1; i++) {
cur *= 9 - i;
res += cur;
}
return res;
}
简单数学题
492.构造矩形
public int[] constructRectangle(int area) {
if (area == 1 || area == 0)return new int[]{area,area};
int[] arr = new int[2];
int dif =Integer.MAX_VALUE ; // 记录差值, 差值最小的就是最好的
for (int i = area/2; i >=1 ; i--) {
if (area % i == 0 && Math.abs(i-(area/i)) < dif){
arr[0] = i > area/i?i:area/i;
arr[1] =i > area/i?area/i:i;
dif = i-(area/i);
}
}
return arr;
}
29.两数相除
507完美数
对于一个 正整数,如果它和除了它自身以外的所有 正因子 之和相等,我们称它为 「完美数」。
给定一个 整数 n, 如果是完美数,返回 true;否则返回 false。
public boolean checkPerfectNumber(int num) {
if (num <= 1)return false;//考虑边界
int isNum =0;
for (int i = num/2; i > 1 ; i--) {
if (num % i == 0){
isNum+=i;
}
}
return isNum+1 == num;
}
快速幂
50 Pow(x, n)
实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。
public double myPow(double x, int n) {
long N = n;
return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
}
public double quickMul(double x, long N) {
if (N == 0) {
return 1.0;
}
double y = quickMul(x, N / 2);
return N % 2 == 0 ? y * y : y * y * x;
}
372超级次方
static final int MOD = 1337;
public int superPow(int a, int[] b) {
int ans = 1;
for (int i = b.length - 1; i >= 0; --i) {
ans = (int) ((long) ans * pow(a, b[i]) % MOD);
a = pow(a, 10);
}
return ans;
}
public int pow(int x, int n) {
int res = 1;
while (n != 0) {
if (n % 2 != 0) {
res = (int) ((long) res * x % MOD);
}
x = (int) ((long) x * x % MOD);
n /= 2;
}
return res;
}