leetcode总结——数字篇

这篇博客总结了LeetCode中关于数字处理的题目,包括翻转数字、整数除法、atoi算法、格雷编码、3的幂次方判断等,探讨了各种算法的实现和思路,例如通过位运算解决范围数字的与和问题,以及如何高效地判断一个数是否是3的幂次方。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数字处理
翻转数字
atoi
范围数字的与和
整数除法
丑数
pow算法:
格雷编码
是否是3的幂次方

众数
超过 ⌊ n/3 ⌋ 次的众数
所有因子

两个恰好出现一次的数
重复三次数字

灯泡开关
计算1的数目
超级丑树

范围数字的与和:
给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。

示例 1:
输入: [5,7]
输出: 4
示例 2:

输入: [0,1]
输出: 0

此题其实就是寻找[m,n]范围内二进制数高位(左边)没有变化的数,后面补上0即为所求的结果。
判断m、n是否相等,如果不相等,m+1会使m的二进制数末位进位,有进位说明m的末位肯定有0的情况,0与任何数相与皆得0,所以结果的末位肯定是0。同理,不断右移1位进行比较,直到最终 m=n 时,说明找到了[m,n]这个范围内高位没有变化的数,左移相同位数得到的结果就是所求的值。

class Solution {
    public int rangeBitwiseAnd(int m, int n) {
        int count = 0; // 统计移位次数
        while (m != n)
        {
            m >>= 1;
            n >>= 1;
            count++;
        }
        n <<= count;
        return n;
    }
}

翻转数字:
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21

解析:难点在于越界值得判断
举个例子。如果说允许的最大值是500的话,那么输入数字是125,逆置为521即越界。所以在
52*10之前进行一次判断。

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

atoi:
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

输入: “42”
输出: 42

示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 ‘-’, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。

示例 3:
输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。

示例 4:
输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。
因此无法执行有效的转换。

示例 5:
输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。

解析:
1:首先需要删除前后空格
2:判断第一个字符是否数字或者符号
3:之后读取后置所有数字,遇到非数字终止
4:读取过程中判断数字大小是否越界

class Solution {
    public int myAtoi(String str) {
        if(str.length()==0)return 0;
        str=str.trim();//去除前后空格
        char c[]=str.toCharArray();        
        if((c[0]<'0'||c[0]>'9')&&c[0]!='-'&&c[0]!='+'){
            return 0;
        }
        int i=0;int flag=1,result=0;//正负标志
        if(c[i]=='-'){i++;flag=-1;}
        else if(c[i]=='+'){i++;}
       while(i<c.length&&c[i]>='0'&&c[i]<='9'){
           // String str1=String.valueOf(c[i]);
          //  int num=Integer.parseInt(str1);
              int num=c[i]-'0';
            if(flag==1&&(result>Integer.MAX_VALUE/10||(result== Integer.MAX_VALUE/10&&num>Integer.MAX_VALUE%10))){return Integer.MAX_VALUE;}
            if(flag==-1&&(-1*result<Integer.MIN_VALUE/10||(-1*result== Integer.MIN_VALUE/10&&-1*num<Integer.MIN_VALUE%10))){return Integer.MIN_VALUE;} 
            result=result*10+num; i++;    
       }
        return flag*result;
    }
}

整数除法:
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以字符串形式返回小数。
如果小数部分为循环小数,则将循环的部分括在括号内。

示例 1:
输入: numerator = 1, denominator = 2
输出: “0.5”

示例 2:
输入: numerator = 2, denominator = 1
输出: “2”

示例 3:
输入: numerator = 2, denominator = 3
输出: “0.(6)”

解析:分为四个部分±号的判别,整数部分[注意越界],".",小数部分

class Solution {
    public String fractionToDecimal(int numerator, int denominator) {
        StringBuilder result=new StringBuilder();
        if(numerator==0)return "0";
        //添加-号
        if(numerator<0^denominator<0){
            result.append("-");
        }
        //获得整数部分,注意越界,−2147483648​
        long  Ldenominator=Math.abs(Long.valueOf(denominator));
        long  Lnumerator=Math.abs(Long.valueOf(numerator));
        result.append(Lnumerator/Ldenominator);
        
        long num1=Lnumerator%Ldenominator;
        if(num1==0)return result.toString();
        //获得小数部分
        result.append(".");
        Map<Long,Integer> map=new HashMap<>();
        StringBuilder fraction=new StringBuilder();
        while(num1!=0){
            if(map.containsKey(num1)){
                fraction.insert(map.get(num1), "(");
                fraction.append(")");
                break;
            }
            map.put(num1,fraction.length());
            num1*=10;
            long num2=num1/Ldenominator;
            fraction.append(String.valueOf(num2));
            num1=num1%denominator;
        }
  
        result.append(fraction);
        return result.toString();
    }
}

丑数:
编写一个程序,找出第 n 个丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。

示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

class Solution {
    public int nthUglyNumber(int n) {
        int dp[]=new int [n];
        int i2=0,i3=0,i5=0;
        dp[0]=1;
        for(int i=1;i<n;i++){
            dp[i]=Math.min(dp[i2] * 2, Math.min(dp[i3] * 3, dp[i5] * 5));
            if(dp[i]==dp[i2]*2)i2++;
            if(dp[i]==dp[i3]*3)i3++;
            if(dp[i]==dp[i5]*5)i5++;
        }
        return dp[n-1];
    }
}

pow算法:
实现 pow(x, n) ,即计算 x 的 n 次幂函数。

示例 1:
输入: 2.00000, 10
输出: 1024.00000

示例 2:
输入: 2.10000, 3
输出: 9.26100

示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25

class Solution {
    public double myPow(double x, int n) {
        long N=n;
        if(n<0){
            N=-N;
            x=1/x;
        }
        return getPow(x,N);
    }
        
        public double getPow(double x,long n){
        if(n==1)return x;
        if(n==0)return 1;
        double value=getPow(x,n/2);
        if(n%2==0){    
            return value*value;
        }
        else{
            return value*value*x;
        }
        }
}

格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。格雷编码序列必须以 0 开头。

示例 1:
输入: 2
输出: [0,1,3,2]
解释:
00 - 0
01 - 1
11 - 3
10 - 2
对于给定的 n,其格雷编码序列并不唯一。
例如,[0,2,3,1] 也是一个有效的格雷编码序列。
00 - 0
10 - 2
11 - 3
01 - 1

示例 2:
输入: 0
输出: [0]
解释: 我们定义格雷编码序列必须以 0 开头。
给定编码总位数为 n 的格雷编码序列,其长度为 2^n。当 n = 0 时,长度为 20 = 1。
因此,当 n = 0 时,其格雷编码序列为 [0]。
处。
解析,其实比较找规律。利用的迭代的方法

class Solution {
    public List<Integer> grayCode(int n) {
        List< Integer> list=new LinkedList<>();
        list.add(0);
        if(n==0)return list;
        list.add(1);
        for(int i=1;i<n;i++){
            int value=1<<i;
            for(int j=list.size()-1;j>=0;j--){
                list.add(list.get(j)+value);
            }
        }
        return list;
    }
}

题目三:
给定一个整数,写一个函数来判断它是否是 3 的幂次方。

示例 1:

输入: 27
输出: true
示例 2:

输入: 0
输出: false
示例 3:

输入: 9
输出: true
示例 4:

输入: 45
输出: false


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

重复三次数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:
输入: [2,2,3,2]
输出: 3

示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99

解析:利用模拟三进制,for (int i = 0; i < 32; i++) 对每个进制位进行计算,nums[j] >>> i & 1就将数字转变成第i位

class Solution {
    public int singleNumber(int[] nums) {
        int ans = 0;
        //考虑每一位
        for (int i = 0; i < 32; i++) {
            int count = 0;
            //考虑每一个数
            for (int j = 0; j < nums.length; j++) {
                //当前位是否是 1
                if ((nums[j] >>> i & 1) == 1) {
                    count++;
                }
            }
            //1 的个数是否是 3 的倍数
            if (count % 3 != 0) {
                ans = ans+(1 << i);
            }
        }
            return ans;
    }
}

众数:
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
示例 1:
输入: [3,2,3]
输出: 3

示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2

class Solution {
    public int majorityElement(int[] nums) {
        Stack<Integer> sta=new Stack<>();
        sta.push(nums[0]);int i=1;
        while(i<nums.length){
            if(sta.isEmpty())sta.push(nums[i]);
            else if(sta.peek()==nums[i])sta.push(nums[i]);
            else sta.pop();i++;
        }
        return sta.peek();
    }
}

超过 ⌊ n/3 ⌋ 次的众数
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
示例 1:
输入: [3,2,3]
输出: [3]
示例 2:
输入: [1,1,1,3,3,2,2,2]
输出: [1,2]

解析:很多细节的地方。比如说虽然是两个值投票,但是刚开始的时候都指向nums[0],关键在于if后一定要直接continue。
并且最后还要遍历一次,因为存储的值不一定是n/3

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        int nums1=0,nums2=0,flag1=0,flag2=0;
        for(int i=0;i<nums.length;i++){
            if(nums1==nums[i]){flag1++;continue;}
            if(nums2==nums[i]){flag2++;continue;}
            if(flag1==0){nums1=nums[i];flag1++;continue;}
            if(flag2==0){nums2=nums[i];flag2++;continue;}
            flag1--;flag2--;
        }
        List<Integer> list=new LinkedList<>();
       flag1 = 0;
       flag2 = 0;
       for (int num : nums) {
           if (num == nums1)
               flag1++;
           else if (num == nums2)
               flag2++;      } 
        if (flag1 > nums.length / 3)
            list.add(nums1);
        if (flag2 > nums.length / 3)
            list.add(nums2);
        return list;
    }
}

所有因子:
整数可以被看作是其因子的乘积。
例如:
8 = 2 x 2 x 2;
= 2 x 4.
请实现一个函数,该函数接收一个整数 n 并返回该整数所有的因子组合。
注意:
你可以假定 n 为永远为正数。
因子必须大于 1 并且小于 n。
示例 1:
输入: 1
输出: []
示例 2:
输入: 37
输出: []
示例 3:
输入: 12
输出:
[
[2, 6],
[2, 2, 3],
[3, 4]
]

class Solution {
    List<List<Integer>> result=new LinkedList<>();
    public List<List<Integer>> getFactors(int n) {
        if(n==1)return result;
        getValue(2,n,n,new LinkedList<>());
        return result;
    }
    public void getValue(int start,int value ,int n,LinkedList<Integer> list){
        if(value==1)
        {result.add(new LinkedList(list));return; }
        for(int i=start;i<n;i++){
            if(value%i==0){
                list.add(i);
                getValue(i,value/i,n,list);
                list.remove(list.size()-1);
            }
        }
    }
}

两个恰好出现一次的数
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
示例 :
输入: [1,2,1,3,2,5]
输出: [3,5]
注意:

class Solution {
  public int[] singleNumber(int[] nums) {
        int xor = 0;
        for (int i : nums)// 一样的抵消,不一样的两个数字异或运算结果必定有一位是1
            xor ^= i;
        int mask = xor & (-xor);
        int[] ans = new int[2];
        for (int i : nums) {
            if ((i & mask) == 0)//== 0、 == mask 两种结果
                ans[0] ^= i;
            else
                ans[1] ^= i;
        }
        return ans;
    }
}

灯泡开关
初始时有 n 个灯泡关闭。 第 1 轮,你打开所有的灯泡。 第 2 轮,每两个灯泡你关闭一次。 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。第 i 轮,每 i 个灯泡切换一次开关。 对于第 n 轮,你只切换最后一个灯泡的开关。 找出 n 轮后有多少个亮着的灯泡。
示例:
输入: 3
输出: 1
解释:
初始时, 灯泡状态 [关闭, 关闭, 关闭].
第一轮后, 灯泡状态 [开启, 开启, 开启].
第二轮后, 灯泡状态 [开启, 关闭, 开启].
第三轮后, 灯泡状态 [开启, 关闭, 关闭].
你应该返回 1,因为只有一个灯泡还亮着。

class Solution {
    public int bulbSwitch(int n) {
        return (int)Math.sqrt(n);
    }
}

计算1的数目
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]

public class Solution {
    public int[] countBits(int num) {
        int[] ans = new int[num + 1];
        int i = 0, b = 1;
        while (b <= num) {
            while(i < b && i + b <= num){
                ans[i + b] = ans[i] + 1;
                ++i;
            }
            i = 0;   
            b <<= 1; 
        }
        return ans;
    }
}

超级丑数:
编写一段程序来查找第 n 个超级丑数。
超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数
示例:
输入: n = 12, primes = [2,7,13,19]
输出: 32
解释: 给定长度为 4 的质数列表 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。
说明:
1 是任何给定 primes 的超级丑数。
给定 primes 中的数字以升序排列。
0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000 。
第 n 个超级丑数确保在 32 位有符整数范围内。
解析:nums为超级丑数的值,pos[i]指的是primes[i]对应nums中的哪一个位置

public class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
        if (n<=1) return 1;
        int[] nums = new int[n];
        nums[0] = 1;
        int[] pos = new int[primes.length];
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer i1, Integer i2) {
               return Integer.compare(nums[pos[i1]] * primes[i1], nums[pos[i2]] * primes[i2]);
            }
        });
        for(int i=0; i<primes.length; i++) minHeap.add(i);
        for(int i=1; i<n; i++) {
            do {
                int min = minHeap.poll();
                nums[i] = nums[pos[min]] * primes[min];
                pos[min] ++;
                minHeap.offer(min);
            } while (nums[i]==nums[i-1]);
        }
        return nums[n-1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值