LeetCode腾讯算法篇之数学与数字(三)

107 阅读4分钟

       数学与数字:Let's play with numbers! 作为一家领先的互联网公司,腾讯也会同别的厂一样经常问及一些数学或者和数字相关的题目。这里我们希望求职者不仅可以做好数学运算的准备,也要巩固一下位运算相关的知识。

(*)问题二十八:整数反转
问题描述:
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。输入: -123
输出: -321注意:假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31,  2^31 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
解题思路一:
字符串反向添加。 步骤一:判断前面有没有+或者-;步骤二:将数字转为String,再转成StringBuilder;步骤三:新建StringBuilder,反向添加之前StringBuilder的每一位的数字;步骤四:使用Integer.ValueOf()进行处理,若出现Execption,则返回零。我的提交执行用时已经战胜 43.89 % 的 java 提交记录。这样的刷题毫无意义。
代码示例一:

    //1.indexOf  检索字符串位置 (查找给定字符串在当前字符串的位置,返回第一个字母所在下标)
    //2.substring(int start,int end)方法用于返回一个字符串的子字符串\
    //3.trim去除字符串两边的空白
    //4.charAt 用于给定位置上的字符
    //5.valueOf()将基本类型转换为字符串
class Solution {
    public int reverse(int x){
        StringBuilder sb = new StringBuilder(String.valueOf(x));
        int plus = 1, i = 0;
        if (sb.charAt(0) == '-') {
            plus = 0;
            i++;
        } else if (sb.charAt(0) == '+') {
            i++;
        }
        StringBuilder sb_02 = new StringBuilder();
        for (int j = sb.length() - 1; j >= i; j--) {
            sb_02.append(sb.charAt(j));
        }
        if (plus == 1) {
            try {
                return Integer.valueOf(sb_02.toString());
            } catch (Exception e) {
                return 0;
            }
        } else {
            try {
                return -Integer.valueOf(sb_02.toString());
            } catch (Exception e) {
                return 0;
            }
        }
    }
}

解题思路二:
使用弹栈压栈的思路。 考虑大于或小于边界的结束条件。我的提交执行用时已经战胜 90.93 % 的 java 提交记录。

            
代码示例二:

class Solution {
    public int reverse(int x) {
        int rev = 0;
        while(x != 0){
            //压栈
            int pop = x % 10;
            x = x / 10;
            //正数溢出的条件,
            if(rev > Integer.MAX_VALUE / 10 || (rev == Integer.MAX_VALUE / 10 && pop > Integer.MAX_VALUE % 10)){
                rev = 0;
                break;
            }else if(rev < Integer.MIN_VALUE / 10 || (rev == Integer.MIN_VALUE / 10 && pop < Integer.MIN_VALUE % 10)){
                rev = 0;
                break;
            }
            //弹栈
            rev = rev * 10 + pop;
        }
        return rev;
    }
}

问题二十九:回文数
问题描述:
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。示例 1:输入: 121输出: true。
解题思路一:
堆栈Stack和队列LinkedList解决(实现了Queue)。 步骤一:负数肯定不行;步骤二:利用除法计算该整数每一位的值,并放进Stack和LinkedList中;步骤三:利用堆栈先进后出和队列先进先出的特性进行每一位的判断。循环结束条件是栈不为空(!stack.isEmpty())。我的提交执行用时已经战胜 56.09 % 的 java 提交记录。
代码示例一:

class Solution {
    public boolean isPalindrome(int x) {
        if(x<0)  return false;
        LinkedList<Integer> linkedlist = new LinkedList<>();
        Stack<Integer> stack = new Stack<>();
        int tmp = 0;
        while(x!=0){
            tmp = x%10;
            x = x/10;
            linkedlist.add(tmp);
            stack.add(tmp);
        }
        while(!stack.isEmpty()){
            if(stack.pop()!=linkedlist.remove()){
                return false;
            }
        }
        return true;
    }
}

解题思路二:
计算前半部分和后半部分是否相等。 步骤一:处理负数、个位数和10的情况;步骤二:计算后半部分,循环结束条件是后半部分大于等于前半部分。步骤三:当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。我的提交执行用时已经战胜 95.17 % 的 java 提交记录。
代码示例二:

 public static boolean isPalindrome(int x) {
        // 特殊情况:
        // 如上所述,当 x < 0 时,x 不是回文数。
        // 同样地,处理10和个位数。
        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;
    }

问题三十:只出现一次的数字
问题描述:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?示例 1:输入: [2,2,1]输出: 1。
解题思路一:
HashSet大法好。 如果已包含该元素,移除掉,否则添加,最后返回hashset.iterator().next()即可。注意如何遍历一个HashSet。使用基于泛型的Iterator遍历器,如果它有next域,直接遍历即可。Iterator<Integer> it = hashset.iterator(); while ( it.hasNext()) {int str = it.next(); },我的提交执行用时已经战胜 24.53 % 的 java 提交记录。很明显使用了额外的空间。
代码示例一:

class Solution {
    public int singleNumber(int[] nums) {
        HashSet<Integer> hashset = new HashSet<>();
        for(int i =0;i<nums.length;i++){
            if(hashset.contains(nums[i])){
                hashset.remove(nums[i]);
            }else{
                hashset.add(nums[i]);
            }
        }
        //如何遍历一个HashSet
        //Iterator<Integer> it = hashset.iterator();
        //while (it.hasNext()) {
        //    int str = it.next();
        //}
        return hashset.iterator().next();
    }
}

解题思路二:
异或位运算。 题目给了那么多已知条件,为什么要.....这么不敏感?异或之后一样的数字都变为零了啊。只剩下唯一出现的那一个元素。我的提交执行用时已经战胜 99.77 % 的 java 提交记录。

                               
代码示例二:

class Solution {
    public int singleNumber(int[] nums) {
        int result = 0;
        for(int i = 0;i<nums.length;i++){
            result  ^= nums[i];
        }
        return result;
    }
}

问题三十一:求众数
问题描述:
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于n/2的元素。你可以假设数组是非空的,并且给定的数组总是存在众数。输入: [3,2,3];输出: 3。
解题思路一:
HashTable大法好。 记住以下几个API:查看当前Table是否包含key:hashmap.containsKey;获取当前key值所对应的value:hashmap.get(nums[i]);给HashTable赋值: hashmap.put(nums[i], ++value)。我的提交执行用时已经战胜 26.08 % 的 java 提交记录。
代码示例一:

class Solution {
    public int majorityElement(int[] nums)  {
        HashMap<Integer, Integer> hashmap = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (!hashmap.containsKey(nums[i])) {
                hashmap.put(nums[i], 1);
            } else {
                //更新hashtable当前的值。
                int value = hashmap.get(nums[i]);
                hashmap.put(nums[i], ++value);
            }
        }
        //考虑总数为奇数或偶数的情况,必须大于等于一半。
        for (int key : hashmap.keySet()){
            int cmp = (nums.length%2==0)?nums.length/2:nums.length/2+1;
            if(hashmap.get(key)>=cmp)
               return key;
        }
        return 0;
    }
}

解题思路二:
采用阵地攻守的思想。 第一个数字作为第一个士兵,守阵地;count = 1;遇到相同元素,count++;遇到不相同元素,即为敌人,同归于尽,count--;当遇到count为0的情况,又以新的i值作为守阵地的士兵,继续下去,到最后还留在阵地上的士兵,是主元素。我的提交执行用时已经战胜 95.61 % 的 java 提交记录。 (解题思路远比代码实现更重要)
代码示例二:

class Solution {
    public int majorityElement(int[] nums)  {
        if(nums.length == 0) return -1;
        int count = 1;
        int nownumber = nums[0];
        for(int i = 1;i<nums.length;i++){
            if(count==0){
                nownumber = nums[i];
                count++;
            }else if(nums[i]!=nownumber){
                count--;
            }else if(nums[i] == nownumber){
                count++;
            }
        }
        return nownumber;
    }
}

解题思路三:

       
代码示例三:

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
}

问题三十二:2的幂
问题描述:
给定一个整数,编写一个函数来判断它是否是 2 的幂次方。输入: 1输出: true解释: 2^0 = 1。
解题思路一:
除二取余法。 步骤一:考虑边界情况,当小于等于零时,返回false,当等于1时,返回true;步骤二:循环除二取余(先取余再除以2),若余数不等于零,返回false。步骤三:循环结束条件使除以2的结果为零,此时如果余数为零,返回true,否则返回false。我的提交执行用时已经战胜 72.20 % 的 java 提交记录2的幂。
代码示例一:

class Solution {
    public boolean isPowerOfTwo(int n){
        if(n<=0) return false;
        if(n==1)  return true;
        int remainder = 0;
        while(n/2!=0){
            remainder=n%2;
            if(remainder != 0) return false;
            n/=2;
        }
        return remainder==0?true:false;
    }
}

解题思路二:
按位相与法。 利用与操作,若一个数n是2的幂次方,则2进制表达式一定为某一位为1,其余为0。则n-1则会变成后面的数全部变成1,原来1的位置变成0。例:n=16的2进制(000010000),则n-1=15的二进制(00001111),则(n&n-1)=0。
代码示例二:

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