美文网首页
Leetcode-Math题型

Leetcode-Math题型

作者: 浩泽Hauser | 来源:发表于2019-06-17 03:37 被阅读0次

    Leetcode 7. Reverse Integer 【Easy】
    难点在于 int 越界问题,用int temp暂时代替即可.

    class Solution {
        public int reverse(int x) {
            int res = 0;
            while(x != 0){
                int a = x % 10;
                int temp = res * 10 + a;    //处理越界问题
                if((temp-a)/10 != res) return 0;
                res = temp;
                x = x/10;
            }
            return res;
        }
    }
    

    Leetcode 165. Compare Version Numbers.【Medium】
    http://www.runoob.com/java/java-regular-expressions.html
    java正则表达式中,. 匹配除"\r\n"之外的任何单个字符。\. 表示匹配 . 本身。为什么是两个 \ 呢?java的 \ 等同于其他语言的一个 \ 。
    难点在于Math.max() , 和 i<v1.length 的判断。

    class Solution {
        public int compareVersion(String version1, String version2) {
            String[] v1 = version1.split("\\.");
            String[] v2 = version2.split("\\.");
            for(int i=0; i<Math.max(v1.length,v2.length);i++){
                int num1 = i<v1.length? Integer.valueOf(v1[i]) : 0;
                int num2 = i<v2.length? Integer.valueOf(v2[i]) : 0;
                if( num1 > num2 ) return 1;
                if( num1 < num2 ) return -1;
            }
            return 0; 
        }
    }
    

    Leetcode 66. Plus One. 【Easy】
    只分为两种情况,一是999,而是其他。
    其他,就用for循环倒序遍历来解决。

    class Solution {
        public int[] plusOne(int[] digits) {
            for(int i=digits.length-1; i>=0; i--){
                if(digits[i]<9){
                    digits[i]++;
                    return digits;
                } else {
                    digits[i] = 0;
                }
            }
            int[] newDigits = new int[digits.length+1];
            newDigits[0] = 1;
            return newDigits;
        }
    }
    

    Leetcode 8. String to Integer (atoi) 【Medium】
    判断str.charAt(0),用start记录数字开始的位置。遍历。

    class Solution {
        public int myAtoi(String str) {
            str = str.trim();
            if(str == null || str.length() == 0) return 0;
            long res = 0; 
            int start = 0; 
            int sign = 1; 
            if(str.charAt(0) == '+'){
                start++;
            } else if (str.charAt(0) == '-'){
                sign = -1;
                start++;
            }
            for(int i = start; i< str.length();i++){
                if(!Character.isDigit(str.charAt(i))){
                    return (int)res * sign;
                }
                res = res * 10 + str.charAt(i) - '0';
                if(sign==1 && res>=Integer.MAX_VALUE) return Integer.MAX_VALUE;
                if(sign==-1 && res>Integer.MAX_VALUE) return Integer.MIN_VALUE;
            }
            return (int)res * sign;
        }
    }
    

    Leetcode 258. Add Digits. 【Easy】
    常规暴力解法。Time: O(n), Space: O(1).

    class Solution {
        public int addDigits(int num) {
            int sum = 0;
            while(true){
                sum = 0;
                while(num>0){
                    sum += num % 10;
                    num /= 10;
                }
                num = sum;
                if(sum<10) break;
            }
            return sum;
            
        }
    }
    

    找规律:以9为循环


    num对应的result以9为循环.jpg
    class Solution {
        public int addDigits(int num) {
            return (num - 1) % 9 + 1;
        }
    }
    

    Leetcode 67. Add Binary 【Easy】
    倒序遍历两个数组。用while(i>=0 || j>=0)作为判断,用remainder记录进位。
    Time: O(n), Space: O(n).

    class Solution {
        public String addBinary(String a, String b) {
            StringBuilder sb = new StringBuilder();
            int i = a.length() - 1;
            int j = b.length() - 1;
            int remainder = 0, sum = 0;
            while(i>=0 || j>=0){
                sum = remainder;
                if(i>=0) sum += a.charAt(i--) -'0';
                if(j>=0) sum += b.charAt(j--) -'0';
                sb = sb.insert(0,sum%2);     
                //如果用sb.append(num%2),就要在最后return sb.reverse().toString()
                remainder = sum/2;  
            }
            if(remainder != 0) sb = sb.insert(0,remainder);
            return sb.toString();
        }
    }
    

    Leetcode 43. Multiply Strings. 【Medium】
    题目要求4: You must not use any built-in BigInteger library or convert the inputs to integer directly. 不能直接用long,int等。
    两个for循环,倒序遍历,每次计算两个一位数的乘积,累加到new int[]对应位置

    class Solution {
        public String multiply(String num1, String num2) {
            int[] digits = new int[num1.length()+num2.length()];  //想清楚这里
            for(int i=num1.length()-1; i>=0; i--){
                for(int j=num2.length()-1; j>=0; j--){
                    int product = (num1.charAt(i)-'0') * (num2.charAt(j)-'0'); //注意要 -'0'
                    int index1 = i + j, index2 = index1 + 1;   //想清楚这里 
                    int sum = product + digits[index2]; 
                    digits[index2] = sum % 10;    
                    digits[index1] += sum / 10;    // 注意是 +=
                }
            }
            
            String res = new String();   // 用String 或 StringBuilder都行
            for(int digit: digits){
                if(!(res.length() == 0 && digit == 0)){   //第一个加入res的必须是非零数
                   res += digit; 
                } 
            }
            System.out.println(res);
            return res.length() == 0? "0": res;        
        }
    }
    

    Leetcode 29. Divide Two Integers 【Medium】
    这题考了左移的操作。用 divideHelper 测试dividend 能装多少个divisor,用的while()循环,实际上是类似二分查找。
    注意:把两个负数传入divideHelper,是因为 MIN_VALUE 绝对值比 MAX_VALUE 大1,如果 abs(MIN_VALUE) 会越界。所以干脆传两个负数进helper.

    class Solution {
        public int divide(int dividend, int divisor) {
            //负数左移,也是相当于*2,直到小于MIN_VALUE,就越界成为正数(不规则的正数)
            if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE;
            boolean isSameSign = (dividend <0) == (divisor<0);
            int res = divideHelper(-Math.abs(dividend),-Math.abs(divisor));        
            return isSameSign ? res : -res;
        }
        
        private int divideHelper(int dividend, int divisor){
            int res = 0;
            int currentDivisor = divisor;
            while(dividend<=divisor){   // abs(divisor) <= abs(dividend)
                int temp = 1;            
                while((currentDivisor << 1)>=dividend && (currentDivisor << 1)<0 ){ //test max temp for: temp * abs(divisor) <= abs(dividend), while temp = 2^n
                    temp <<=1;
                    currentDivisor <<=1;
                }
                dividend -= currentDivisor;
                res += temp;
                currentDivisor = divisor;   //将currentDivisor恢复为divisor
            }       
            return res;
        }    
    }
    

    Leetcode 69. Sqrt(x). 【Easy】
    这题,需要写出两种情况:1) 二分法;2) 牛顿法。
    二分法:Time: O(logn), Space: O(1).

    class Solution {
        public int mySqrt(int x) {
            if(x<=0) return 0;
            int low = 1, high = x;
            while(low<=high){
                long mid = (high - low)/2 + low;
                if(mid * mid == x){     // mid * mid 可能越界,所以应该设置mid为long。比如当x=MAX_VALUE, 第一次计算mid^2就必然越界
                    return (int)mid;
                } else if (mid * mid > x){
                    high = (int)mid - 1;
                } else {
                    low = (int)mid + 1;
                }
            }
            return high;
            // if(high * high < x){ //必定的结果
            //     return high;
            // } 
            // return 0;
        }
    }
    

    牛顿法:
    牛顿法,又名切线法。

    CSDN搜索: leetcode:Sqrt(x) 牛顿迭代法求整数开方
    https://blog.csdn.net/newfelen/article/details/23359629

    draw a tangent line for function f(x), the tangent line and x axis intersects at point( xn+1, 0). So the tangent line passes both (xn, f(xn)) and (xn+1, 0). We can get the equation for the tangent line, which is ...

    公式推导:
    经过 (xn+1,0), ( xn, f(xn) ) 且斜率为f'(xn) 的直线,直线表达式为:
    ( f(xn)-0 ) / (xn - xn+1) = f'(xn) , 化简得到:


    牛顿法的迭代公式.jpg

    而本题 f(xn) = x^2 - n, f' = 2x. 所以,
    xn+1 = xn - (x^2-n)/2x
    = (x + n/x)/2.
    牛顿法的Time Complexity 是unstable 不稳定的,可以理解为 接近O(logn).

    public int mySqrt(int x){
        long res = x; 
        while( res * res > x){
            res = ( res + x / res ) / 2 ; 
        }
        return (int)res;
    }
    

    Leetcode 367. Valid Perfect Square. 【Easy】
    这题,需要写出两种情况:1) 二分法;2) 牛顿法。
    二分法:Time: O(logn), Space: O(1).

    class Solution {
        public boolean isPerfectSquare(int num) {
            int low = 1, high = num;
            while(low<=high){
                long mid = (high - low)/2 + low;
                if(mid * mid == num){     // mid * mid 可能越界,所以应该设置mid为long。比如当x=MAX_VALUE, 第一次计算mid^2就必然越界
                    return true; 
                } else if (mid * mid < num){
                    low = (int)mid + 1;
                } else {
                    high = (int)mid - 1;
                }     
            }
            return false;
        }
    }
    

    牛顿法:Time 是 uncertain,但在这题可理解为优于O(nlogn).

    class Solution {
        public boolean isPerfectSquare(int num) {
            long x = num;
            while(x * x > num){
                x = (x + num/x)/2;
            }
            return x * x == num;
        }
    }
    

    Leetcode 50. Pow(x, n) 【Medium】

    Time: O(logn), Space: O(1).
    本题暴力解法就是O(n), 要提升,必然是二分法,提升到O(logn).
    用Iterative方法,每次while循环,让n降低到一半,并对x进行平方。
    注意:min的绝对值大于max绝对值,所以Integer.MIN_VALUE的绝对值会越界;所以要用long值记录n的绝对值。

    class Solution {
        public double myPow(double x, int n) {
            if(n==0) return 1;
            long abs = Math.abs((long)n);
            double res = 1;
            while(abs >0){
                if(abs%2 != 0) {
                    res *= x;
                }
                x *= x;
                abs /= 2;
            }
            if(n<0) {
                return 1/res;
            } else {
                return res;
            }       
        }
    }
    

    【不推荐】用Recursive方法做。

        public double myPow(double x, int n) {
            return n<0? 1/helper(x,-n):helper(x,n);                
        }
           
        private double helper(double x, int n){
            if(n==0) return 1;
            double y = helper(x,n/2);
            if(n%2 != 0){
                return y * y * x;
            } else {
                return y * y;
            }
        }
    

    Leetcode 365. Water and Jug Problem 【Medium】
    这是纯数学问题。只有当z%(x和y的最大公约数)==0 时才为true。
    math theory 链接:Bézout's identity 数学家 Mathematician 读音:/'bezu/ 贝祖

    class Solution {
        public boolean canMeasureWater(int x, int y, int z) {
            if(x+y<z) return false;
            if(x==z || y==z || x+y==z) return true;
            return z % gcd(x,y) == 0;
        }
        //辗转相除法,iterative方法。
        public int gcd(int a, int b){
            while(b!=0){
                int temp = b;
                b = a%b;
                a = temp;
            }
            return a;
        }
    }
    

    辗转相除法,英文是Euclidean Algorithm,欧几里得算法。【juːˌklɪdiən】
    最大公约数的Recursive写法:

        public int gcd(int a, int b){
            if(b==0) return a;
            return gcd(b,a%b);
        }
    

    Leetcode 204 Count Primes 【Easy】
    构建一个boolean[] 用来记录每个数是否为prime。外层for循环遍历数组,内层for循环, 改变 i的倍数对应的boolean,从而得到每个数是否为prime的信息。

    class Solution {
        public int countPrimes(int n) {
            boolean[] notPrime = new boolean[n+1]; //j<=n/i情况下用n+1.若是i*j<n,用n就行
            int res = 0;
            for(int i=2;i<n;i++){
                if(notPrime[i]==false){
                    res++;
                    // System.out.println(i);
                    for(int j=2;j<=n/i;j++){   //或者: (int j=2,i*j<n,j++)也可
                        notPrime[i*j] = true;
                    }
                }
            }
            return res;
        }
    }
    

    Leetcode 1. Two Sum. 【Easy】
    经典题,用HashMap来记录前面存在的值和对应位置。for循环遍历至某值,查询target-该值在map中是否存在。

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int[] res = new int[]{-1,-1};
            Map<Integer,Integer> map = new HashMap<>();
            for(int i=0; i<nums.length; i++){
                if(map.containsKey(target-nums[i])){
                    res[0] = map.get(target-nums[i]);
                    res[1] = i;
                    break;
                }
                map.put(nums[i],i);
            }
            return res;
        }
    }
    

    Leetcode 167. Two Sum II - Input array is sorted. 【Easy】
    Time: O(n).
    题目:已经排序完毕。
    经典题,参考了二分法的步骤,但这里是right--或left++,每次只移动一步

    class Solution {
        public int[] twoSum(int[] numbers, int target) {
            int left = 0, right = numbers.length-1;
            while(left<right){
                if(numbers[left]+numbers[right]==target){
                    return new int[]{left+1,right+1};
                } else if(numbers[left]+numbers[right]>target){
                    right--;
                } else {
                    left++;
                }
            }
            return new int[]{-1,-1};
        }
    }
    

    Leetcode 15. 3Sum. 【Medium】
    Time: O(n2).
    题目没排序。要用二分法思想,需要先排序。只有在从小到大排序好的数组中,才能移动left和right,而且才能达到避免duplicate的目的。
    参考Leetcode 167. 同样是运用了二分法的思想。难点在于怎么把三个数之和转化为两个数之和。
    要避免duplicate重复,首先需要排序。用Arrays.sort() 排序。
    然后第一层for循环遍历数组,第二层while用left和right,遍历该数右边的子数组。

    class Solution {
        public List<List<Integer>> threeSum(int[] nums) {
            List<List<Integer>> res = new ArrayList<>();
            Arrays.sort(nums);
            for(int i=0; i<nums.length-2; i++){
                if(i>0 && nums[i]==nums[i-1]) continue; //避免duplicate的关键1
                int left = i+1, right = nums.length-1;
                int sum = 0 - nums[i];
                while(left<right){
                    if(nums[left]+nums[right]==sum){
                        res.add(Arrays.asList(nums[i],nums[left],nums[right]));  //Arrays.asList().  
                        //两个while循环是避免duplicate的关键2
                        while(left<right && nums[left+1]==nums[left]) left++;    //left<right是防止越界
                        while(left<right && nums[right-1]==nums[right]) right--; //left<right是防止越界
                        left++;
                        right--;
                    } else if(nums[left]+nums[right]>sum){
                        right--;
                    } else {
                        left++;
                    }
                }
            }
            return res;
        }
    }
    

    Leetcode 16.
    题目没排序。要用二分法思想,需要先排序。只有在从小到大排序好的数组中,才能移动left和right。

    class Solution {
        public int threeSumClosest(int[] nums, int target) {
            int res = nums[0] + nums[1] + nums[2];
            Arrays.sort(nums);
            for(int i=0; i<nums.length-2;i++){
                int left = i+1, right = nums.length-1;
                while(left<right){
                    int sum = nums[i]+nums[left]+nums[right];
                    if(Math.abs(sum-target)<Math.abs(res-target)){
                        res = sum;
                    }
                    if(sum>target){
                        right--;
                    } else {
                        left++;
                    }
                }
            }
            return res;
        }
    }
    

    Leetcode 259. 3Sum Smaller 【Medium】

    class Solution {
        public int threeSumSmaller(int[] nums, int target) {
            int res = 0; 
            Arrays.sort(nums);
            for(int i=0;i<nums.length-2;i++){
                int left = i+1, right = nums.length-1;
                while(left<right){
                    int sum = nums[i]+nums[left]+nums[right];
                    if(sum<target){
                        res += right-left; //特定的i,特定的left,right(right可以一直左移到left+1,共right-left个)
                        left++;        //测试下一个left
                    } else {
                        right--; //right左移,直到找到符合条件的right使得sum<target
                    }
                }
            }
            return res;        
        }
    }
    

    Leetcode 18. 4Sum. 【Medium】

    class Solution {
        public List<List<Integer>> fourSum(int[] nums, int target) {
            List<List<Integer>> list = new ArrayList<>();
            Arrays.sort(nums);
            for(int i=0;i<nums.length-3;i++){
                if(i>0 && nums[i]==nums[i-1]) continue;
    //比3Sum多了一个for和一句continue
                for(int j=i+1; j<nums.length-2;j++){
                    if(j>i+1 && nums[j]==nums[j-1]) continue;
                    int left = j+1, right = nums.length-1;
                    while(left<right){
                        int sum = nums[i]+nums[j]+nums[left]+nums[right];
                        if(sum==target){
                            list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                            while(left<right && nums[left+1]==nums[left])   left++;
                            while(left<right && nums[right-1]==nums[right]) right--;
                            left++;
                            right--;
                        } else if(sum<target){
                            left++;
                        } else {
                            right--;
                        }   
                    }                
                }
            }
            return list;
        }
    }
    

    相关文章

      网友评论

          本文标题:Leetcode-Math题型

          本文链接:https://www.haomeiwen.com/subject/tkixfctx.html