刷穿剑指:二进制篇

 如果你也在为数据结构感到苦恼,那就和我一起刷穿🗡☞

个人主页:Pretend..
系列文章:刷穿🗡☞

 想要最好的生活,那就先让生活看到最好的你

目录

1、只出现一次的数字

2、前n个数二进制形式中1的个数

方法一:根据"i&(i-1)"计算 

方法二:根据“i/2”计算 

3、单词长度的最大乘积


二进制的位运算:与、或、非、异或、左移、右移。

与、或、异或的运算规律
与(&)或(|)异或(^)
0&0=00|0=00^0=0
1&0=01|0=11^0=1
0&1=00|1=10^1=1
1&1=11|1=11^1=0

左移运算符:左移n位,左边n位被丢弃,右边补n个0。

右移(右移需要正负):正数右移,右边丢弃,左边补0;负数右移,右边丢弃,左边补1。

Java中增加无符号右移操作符(">>>"):无论正数还是负数,都在最左边插入0。

1、只出现一次的数字

  【题目描述】找出数组中只出现了一次的数字。(其它数字都出现了三次)

分析:

之前有一个简化版本的“数组中只有一个数组出现一次,其余的都出现两次”。任何一个数组异或自己的结果都为0.则将数组中所有的数字进行异或,最终的结果就是我们要找的这个数字。

但是,对于这个题来说,其它数字都出现三次,则无法使用异或运算来进行计算。

一个整数是由32个0或1组成。我们可以将数组中所有元素的同一个位置的数位相加,如果第i位相加之和可以被3整除(因为数组中只有一个元素出现一次,其余的出现三次,所以余数只能是0或1),那么只出现一次的数字的第i位一定是0;如果不能被三整除,则只出现一次的数字第i位为1。这样我们就可以推出只出现一次的数字的所有二进制位,进而知道它的值。

【代码如下】

class Solution {
    //只有一个数出现一次(x),其余的出现三次,则将其转换为二进制,按位相加,如果其位能整除3,则x该二进制位为0;否则该二进制位为1
    public int singleNumber(int[] nums) {
        int[] array=new int[32];
        for(int j=0;j<nums.length;++j){
            for(int i=0;i<32;++i){
                //获取数组中下标为j的元素的从前往后数第i位的值
                array[i]+=nums[j]>>(31-i)&1;
            }
        }
        int ret=0;
        for(int i=0;i<32;++i){
            //此时array数组中存储的是只出现一次的数组的二进制的每一位
            ret=(ret<<1)+(array[i]%3==0?0:1);
        }
        return ret;
    }
}

【拓展】这个方法可以计算查找中唯一一个出现m次的数字

输入一个整数数组,数组中只有一个数组出现m次,其他数字出现n次。找出那个出现m次的数字m不能被n整除)。

【分析】如果数组中所有数字的二进制数的第i位相加之和能被n整除,那么出现m次的数字第i位一定为0;否则出现m次的数字第i位位1;

2、前n个数二进制形式中1的个数

 【题目描述】输入一个非负数n,计算0到n之间每个数字的二进制形式中1的个数,输出为一个数组。

【分析】可以使用一个for循环来遍历从0到n这n个数。问题转化为如何求一个整数二进制中1的个数。(以下介绍的两种方法时间复杂度达到了O(n),对于这道题普通的方法,时间复杂度会达到O(nk))。

方法一:根据"i&(i-1)"计算 

"i&(i-1)"是将i的二进制中最右边的1变为0。也就是说,整数i的二进制中1的个数比"i&(i-1)"的二进制中1的个数多1。

【代码如下】

class Solution {
    public int[] countBits(int n) {
        int[] array=new int[n+1];
        for(int i=1;i<=n;++i){
            array[i]=array[i&(i-1)]+1;
        }
        return array;
    }
}

方法二:根据“i/2”计算 

如果正整数i是一个偶数,那么i就等于“i/2”左移一位,因此i和“i/2”的二进制中1的个数相等;如果i为奇数,那么i就等于“i/2"左移一位再加1,所以奇数i二进制中1的个数比“i/2”中1的个数多1。例如,我们可以根据3的二进制中1的个数求出6、7二进制中1的个数。

【代码如下】

class Solution {
    public int[] countBits(int n) {
        int[] array=new int[n+1];
        for(int i=1;i<=n;++i){
            if(i%2==1){
                array[i]=array[i/2]+1;
            }else{
                array[i]=array[i/2];
            }
        }
        return array;
    }
}

3、单词长度的最大乘积

 【题目描述】给定一个字符串数组,计算不包含相同字符的两个字符串的长度的最大乘积。如果所有字符串都包含相同字符,则返回0。(假设字符串中只包含英文小写字母)

本题只需要考虑26个小写字母,我们只需要26个二进制数位来代替boolean类型记录字符串中出现的字符。0表示false,1表示true。

可以用一个int类型的整数来记录某个字符串中出现的字符。如果字符中包含'a',则整数右边第一位为1,如果包含'b',则整数中右边倒数第二位为1,以此类推。这样做能更好的判断两个字符串是否包含相同的字符。如果两个字符串中包含相同的字符,则它们对应的整数的某个数位都为0,即这两个整数进行与运算的结果不为0;如果不包含相同的字符,则两个整数的与运算的结果为0.

【代码如下】

class Solution {
    public int maxProduct(String[] words) {
         int[] array=new int[words.length];
         for(int i=0;i<words.length;++i){
             for(char ch:words[i].toCharArray()){
                 //注意:这只能用与运算,不能用+运算,否则,如果出现重复字符,会出错
                 array[i]|=1<<(ch-'a');
             }
         }
         int ret=0;
         for(int i=0;i<words.length;++i){
             for(int j=i+1;j<words.length;++j){
                 if((array[i]&array[j])==0){
                     int len=words[i].length()*words[j].length();
                     ret=ret>len?ret:len;
                 }
             }
         }
         return ret;
    }
}
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢敲代码的大秃噜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值