算法必刷系列之数字与数学

文章目录

  • 数字与数学
    • 符号统计
    • 阶乘0的个数
    • 整数反转
    • 字符串转数字
    • 判断回文数字
    • 十进制转七进制
    • 进制转换
    • 数组实现整数加法
    • 字符串加法
    • 二进制求和
    • 求2的幂
    • 求3的幂
    • 求4的幂
    • 最大公约数
    • 最小公倍数
    • 判断质数
    • 质数计数
    • 判断丑数
    • 丑数计数

数字与数学

数字与数学的问题基础且庞大,算法问题中,一般涉及幂运算、阶乘、初等数论,如最大公约数、质数判断与计数等基础问题。

符号统计

leetcode1822
只要乘数中存在一个0,结果为0,整数不影响结果符号,偶数个负数相乘,结果为正,奇数个负数相乘结果为负

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

阶乘0的个数

leetcode16.05
阶乘0的个数取决于2和5出现的次数,而5出现的次数明显大于2,所以只需要统计5,25,125等出现的次数即可。

public int trailingZeroes(int n) {
    int res = 0;
    for(long i=5;n/i>0;i*=5){
        res+=n/i;
    }
    return res;
}

整数反转

leetcode7
反转的过程就是原数字对10取余数,得到末尾数字,新数字乘10加上末尾数字。需要注意反转过程中的数字溢出问题。

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

字符串转数字

leetcode8
按照题目要求,以char数组的形式遍历字符串,去掉空白字符,设置正负数符号,字符➖字符0获得数值,乘10累加,注意数字溢出

public int myAtoi(String s) {
    int n = s.length();
    char[] chars = s.toCharArray();
    int sign = 1;
    int res = 0;
    int index = 0;
    while (index < n && chars[index] == ' ') {
        index++;
    }
    if (index == n) {
        return res;
    }
    if (chars[index] == '+') {
        index++;
    }else if (chars[index] == '-') {
        sign = -1;
        index++;
    }
    while (index < n) {
        if (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++;
        } else {
            return res;
        }
    }
    return res;
}

判断回文数字

leetcode9
将数字反转一般并比较是否相等

public boolean isPalindrome(int x) {
    if(x<0 || x%10==0&&x!=0){
        return false;
    }
    int reverseNum=0;
    while(x>reverseNum){
        reverseNum = reverseNum*10+x%10;
        x/=10;
    }
    return x==reverseNum || x==reverseNum/10;
}

十进制转七进制

leetcode504
采用除商取余法,保存余数,最后将余数反转

public String convertToBase7(int num) {
    if(num==0){
        return "0";
    }
    boolean sign = true;
    if(num<0){
        num = -num;
        sign=false;
    }
    StringBuilder sb = new StringBuilder();
    while(num!=0){
        sb.append(num%7);
        num /= 7;
    }
    if(!sign){
        sb.append("-");
    }
    return sb.reverse().toString();
}

进制转换

采用除商取余法,保存余数,最后将余数反转,同时设置常数数组,保存A-F常数值,用来保存十六进制转换结果

public String convert(int num,int N) {
    final String[] F = new String[]{"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
    if(num==0){
        return "0";
    }
    boolean sign = true;
    if(num<0){
        num = -num;
        sign=false;
    }
    StringBuilder sb = new StringBuilder();
    while(num!=0){
        sb.append(F[num%N]);
        num /= N;
    }
    if(!sign){
        sb.append("-");
    }
    return sb.reverse().toString();
}

数组实现整数加法

leetcode66
从末尾开始加1,如果有进位,前一位继续加1,直至加到数组下标为0的位置,如果仍有进位,创建新数组,设置下标0的位置值为1,并返回。

public int[] plusOne(int[] digits) {
    for(int i = digits.length - 1;i>=0;i--){
        digits[i]++;
        digits[i]%=10;
        if(digits[i]!=0){
            return digits;
        }
    }
    digits = new int[digits.length+1];
    digits[0] = 1;
    return digits;
}

字符串加法

从末尾开始遍历两个字符串,转换为数值,进行求和,计算进位,保存当前位,最后反转

public String addStrings(String num1, String num2) {
    int i = num1.length() - 1;
    int j = num2.length() - 1;
    int add = 0;
    StringBuilder sb = new StringBuilder();
    while (i >= 0 || j >= 0 || add != 0) {
        int x = i >= 0 ? num1.charAt(i) - '0' : 0;
        int y = j >= 0 ? num2.charAt(j) - '0' : 0;
        int res = x + y + add;
        add = res / 10;
        sb.append(res % 10);
        i--;
        j--;
    }
    return sb.reverse().toString();
}

二进制求和

leetcode67
与字符串求和本质相同,从末尾开始遍历两个字符串,转换为数值,进行求和,计算进位,保存当前位,最后反转

public String addBinary(String a, String b) {
    int i = a.length()-1;
    int j = b.length()-1;
    int ca = 0;
    StringBuilder sb = new StringBuilder();
    while(i>=0||j>=0||ca!=0){
        int x = i>=0?a.charAt(i)-'0':0;
        int y = j>=0?b.charAt(j)-'0':0;
        int res = x+y+ca;
        ca = res/2;
        sb.append(res%2);
        i--;
        j--;
    }
    return sb.reverse().toString();
}

求2的幂

leetcode231
求幂运算的模板如下,可能针对不同的数字有其独特的巧妙解法

public boolean isPowerOfTwo(int n) {
    if(n<=0){
        return false;
    }
    while(n%2==0){
        n/=2;
    }
    return n==1;
}

求2的幂巧妙解法,2的幂的二进制最高位为1,其余为为0,即n&n-1=0

public boolean isPowerOfTwo(int n) {
    return n>0 && (n&(n-1))==0;
}

求3的幂

leetcode326
模板

public boolean isPowerOfThree(int n) {
        if(n<=0){
            return false;
        }
        while(n%3==0){
            n/=3;
        }
        return n==1;
    }

在int类型范围内,3的幂的最大整数是1162261467,故只要是3的幂,一定可以被1162261467整除

public boolean isPowerOfThree(int n) {
    return n > 0 && 1162261467 % n == 0;
}

求4的幂

leetcode342
模板

public boolean isPowerOfFour(int n) {
    if(n<=0){
        return false;
    }
    while(n%4==0){
        n/=4;
    }
    return n==1;
}

4的幂一定是2的幂,且满足最高位为1,后面有偶数个0,可以通过n&0xaaaaaaaa来实现

public boolean isPowerOfFour(int n) {
    return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;
}

最大公约数

碾转相除法

public int gcd(int a, int b) {
    int k = 0;
    while (k!=0){
        k = a%b;
        a = b;
        b = k;
    };
    return a;
}

最小公倍数

两数乘积除以最大公约数

public int mcl(int a, int b) {
    int gcd = gcd(a, b);
    return a * b / gcd;
}

判断质数

从2开始,逐个将待判断的数除以从2到其平方根范围内的所有整数(包括平方根),如果能够整除,则该数不是质数。如果在整个范围内都没有找到能整除的数,则该数是质数。

public boolean isPrime(int num){
    int max = (int)Math.sqrt(num);
    for (int i = 2; i<=max;i++){
        if(num%i==0){
            return false;
        }
    }
    return true;
}

质数计数

leetcode204
循环遍历判断

public int countPrimes(int n) {
    int count = 0;
    for(int i=2;i<n;i++){
        if(isPrime(i)){
            count++;
        }
    }
    return count;
}

public boolean isPrime(int num){
    int max = (int)Math.sqrt(num);
    for (int i = 2; i<=max;i++){
        if(num%i==0){
            return false;
        }
    }
    return true;
}

设置一个长度为n的数组,初始时,设置数组全为1,之后,从下标2开始遍历,如果数组当前值为1,质数计数器➕1,并将当前数字的倍数以及与倍数相关的非质数下标设置为0,最后返回质数计数器的值。

public int countPrimes(int n) {
    int[] isPrime = new int[n];
    int count = 0;
    Arrays.fill(isPrime,1);
    for(int i=2;i<n;i++){
        if(isPrime[i]==1){
            count++;
            if((long)i*i<n){
                for(int j=i*i;j<n;j+=i){
                    isPrime[j]=0;
                }
            }
        }
    }
    return count;
}

判断丑数

leetcode263
可以直接根据丑数的概念进行判断。如果不包含质因数或者包含质因数 2、3 和 5 的正整数即为丑数

public boolean isUgly(int n) {
    if(n<=0){
    	return false;
    }
    int[] factors = new int[]{2,3,5};
    for(int factor:factors){
        while(n%factor==0){
            n = n/factor;
        }
    }
    return n==1;
}

丑数计数

leetcode264
循环遍历判断

public int nthUglyNumber(int n) {
    int count = 0;
    int i=1;
    while(true){
        if(isUgly(i)){
            count++;
            if(count==n){
                return i;
            }
        }
        i++;
    }
}

public boolean isUgly(int num){
    if(num<=0){
        return false;
    }
    int[] factors = new int[]{2,3,5};
    for(int factor:factors){
        while(num%factor==0){
            num = num/factor;
        }
    }
    return num==1;
}

设置一个小顶堆,初始时,将最小的丑数1加入队中,循环n次,每次将堆顶元素x移除,每次将2x,3x,5x放入堆中,为了避免重复,可以使用集合过滤,循环n次之后,最小的第n个丑数出堆返回

public int nthUglyNumber(int n) {
    int[] factors = {2, 3, 5};
    Set<Long> seen = new HashSet<Long>();
    PriorityQueue<Long> heap = new PriorityQueue<Long>();
    seen.add(1L);
    heap.offer(1L);
    int ugly = 0;
    for (int i = 0; i < n; i++) {
        long curr = heap.poll();
        ugly = (int) curr;
        for (int factor : factors) {
            long next = curr * factor;
            if (seen.add(next)) {
                heap.offer(next);
            }
        }
    }
    return ugly;
}

你可能感兴趣的:(算法必刷系列,算法)