跟孙哥学java
数学是学生时代掉头发的学科,算法是毕业后掉头发的学科。而两者又是相通的,很多算法本来就是数 学问题,而很多数学问题也需要借助算法才能用代码实现。数学的门类很多,涉及的范围很广,很多难 度也超大,但是在算法中,一般只会选择各个学科的基础问题来考察,例如素数问题、幂、对数、阶 乘、幂运算、初等数论、几何问题、组合数学等等,本章还是选择最热门最重要的问题来讲解。
- 1. 数字里统计的处理技巧
- 2. 必须熟练掌握溢出的处理方法
- 3. 掌握进制的处理方法
数字统计专题
统计一下特定场景下的符号,或者数字个数等是一类非常常见的问题。如果按照正常方式强行统计,可能 会非常复杂,所以掌握一些技巧是非常必要的。
符号统计
LeetCode1822给定一个数组,求所有元素的乘积的符号,如果最终答案是负的返回-1,如果最终答案是 正的返回1,如果答案是0返回0。 仔细分析一下这道题目,如果将所有数都乘起来,再判断正负,工作量真不少,而且还可能益出。我们发 现,一个数如果是-100和-1,对符号位的贡献是完全一样的,所以只需要看有多少个负数,就能够判断 最后乘积的在符号了。
class Solution {
public int arraySign(int[] nums) {
int sign=1;
for (int i = 0; i < nums.length; i++) {
if(nums[i]==0) {
return 0;
}else if(nums[i]<0){
sign=-1*sign;
}
}
return sign;
}
}
阶乘0的个数
很多数学相关算法的关键在于找到怎么通过最简洁的方式来解决问题,而不是硬算。例如:面试题16.05: 设计一个算法,算出阶乘有多少个尾随零。 这个题如果硬算,一定会超时,其实我们可以统计有多少个0,实际上是统计2和5一起出现多少对,不 过因为2出现的次数一定大于5出现的次数,因此我们只需要检查5出现的次数就好了,那么在统计过程 中,我们只需要统计5、10、15、25、…5^这样5的整数倍项就好了,最后累加起来,就是多少个 0。代码就是:
class Solution {
public int trailingZeroes(int n) {
// int res=0;
// int five=5;
// while (five<=n){
// res=res+n/five;
// five=five*5;
// }
// return res;
int cnt=0;
//num设置为long,因为leetocode int会溢出 通过不了
for(long num=5;n/num>0;num*=5){
//依次判断5,25,125 因为25=5*5,会出现两次2*5
cnt+=n/num;
}
return cnt;
}
}
溢出问题
溢出问题是一个极其重要的问题,只要涉及到输出一个数字,都可能遇到,典型的题目有三个:数字反
转,将字符串转成数字和回文数。不过谥出问题一般不会单独考察,甚至面试官都不会提醒你,但他就像
捕捉猎物一样町着你,看你会不会想到有谥出的问题,例如这道题是一个小伙伴面美团时拍的。所以凡是
涉及到输出结果为数字的问题,必须当心!
整数反转
LeetCode7给你一个32位的有符号整数X,返回将x中的数字部分反转后的结果。如果反转后整数超过 32位的有符号整数的范围[-2^31,2^31-1],就返回0。假设环境不允许存储64位整数(有符号或无 符号)。 整数反转 这个题的关键有两点,一个是如何进行数字反转,另一个是如何判断益出。反转好说,那为什么会有益出 问题呢?例如1147483649这个数字,它是小于最大的32位整数2147483647的,但是将这个数字反转过 来后就变成了9463847411,这就比最大的32位整数还要大了,这样的数字是没法存到t里面的,所以就 溢出了。 首先想一下,怎么去反转一个整数。用栈?或者把整数变成字符串再反转字符串?都可以但都不好。我们 只要一边左移一边处理末尾数字就可以了。以12345为例,先拿到5,再拿到4,之后是3,2,1,然后就可 以反向拼接出一个数字了。那如何获得未尾数字呢?好办,循环取模运算即可。例如:
1.将12345%10得到5,之后将12345/10=1234
2.将1234%10得到4,再将1234/10=123
3.将123%10得到3,再将123/10=12
4.将12%10得到2,再将12/10=1
5.将1%10得到1,再将1/10=0
画图就是
这样的话,是不是将循环的判断条件设为X>0就可以了呢?不行!因为忽略了负数的问题,应该是
whil(x!=0)。去掉符号,剩下的数字,无论正数还是负数,按照上面不断的/10这样的操作,最后都会变
成0,所以判断终止条件就是!=0。有了取模和除法操作,就可以轻松解决第一个问题,如何反转。
接下来看如何解决益出的问题。我们知道32位最大整数是MAX=2147483647,如果一个整数
num>MAX,那么应该有以下规律:
uMs/10>MAX/10=214748364,也就是如果底数第二位大于4了,不管最后一位是什么都已经益出
了,如下:
所以我们要从到最大数的1/10时,就要开始判断,也即:
·如果um>214748364那后面就不用再判断了,肯定益出了。
·如果um=214748364,这对应到上图中第三、第四、第五排的数字,需要要跟最大数的末尾数字比
较,如果这个数字比7还大,说明益出了。
·如果num<214748364,则没问题,继续处理。
这个结论对于负数也是一样的,所以实现代码就是:
class Solution {
public int reverse(int x) {
int res=0;
while (x>0){
int num=x%10;
if(num==0&&res==0){
}else {
if(res>Integer.MAX_VALUE/10||(res==Integer.MAX_VALUE/10&&num>Integer.MAX_VALUE%10)){
return 0;
}
res=res*10+num;
}
x=x/10;
}
while (x<0){
int num=x%10;
if(num==0&&res==0){
}else {
if(res<Integer.MIN_VALUE/10||(res==Integer.MIN_VALUE/10&&num<Integer.MIN_VALUE%10)){
return 0;
}
res=res*10-num;
}
x=x/10;
}
return res;
}
}
字符串转整数
class Solution {
public int myAtoi(String s) {
char[] chars = s.toCharArray();
int len=chars.length;
int index=0;
while (index<len&&chars[index]==' '){
index++;
}
if(index==len){
return 0;
}
int sign=1;
if(chars[index]=='+'){
index++;
}else if(chars[index]=='-'){
sign=-1;
index++;
}
int res=0;
while (index<len&&chars[index]>='0'&&chars[index]<='9'){
if(res>Integer.MAX_VALUE/10||(res==Integer.MAX_VALUE/10&&(chars[index]-'0')>Integer.MAX_VALUE%10)){
return Integer.MAX_VALUE;
}
if(res<Integer.MIN_VALUE/10||(res==Integer.MIN_VALUE/10&&(chars[index]-'0')>-(Integer.MIN_VALUE%10))){
return Integer.MIN_VALUE;
}
res=res*10+sign*(chars[index]-'0');
index++;
}
return res;
}
}
进制专题
进制问题也是一个非常重要的专题,有的直接处理还挺费劲,我们看两道题。
七进制数
七进制数 LeetCode504.给定一个整数num,将其转化为7进制,并以字符串形式输出。其中-10^7<=num<= 10^7。 示例 1: 输入: num = 100 输出: "202" 示例 2: 输入: num = -7 输出: "-10"
class Solution {
public String convertToBase7(int num) {
if(num==0) return "0";
Stack<Integer> stack=new Stack<>();
StringBuilder sb=new StringBuilder();
if(num<0) {
sb.append("-");
}
while (num!=0){
int x = num % 7;
stack.push(x);
num=num/7;
}
while (!stack.isEmpty()){
sb.append(Math.abs(stack.pop()));
}
return sb.toString();
}
}