位运算相关内容

1.二进制中1的个数_牛客网

        输入一个整数 n ,输出该数32位二进制表示中1的个数。其中负数用补码表示。

方法1:将数字与1进行按位与,然后证书右移一位,继续与1按位与,按位与结果是1表示二进制位是1,计数即可。

方法2:

        当二进制中1密集,方法一效率高,但当0比重高时,方法一检测大部分筛选都无效,效率不高,观察如下按位与过程:

        原x中只有三个1,而它不断与x-1进行按位与数字就置零了,统计出来刚好与了三次,此时最小成本最小次数。【WHY:数字减1会向高位借位,二进制11000减一是10111,这样相当于原始值局部比特位进行了反转(最低位到碰到第一个1的所有位的1和0值反转),所以相与后这部分都会抵消变为0,这样就直接检测到了从低位开始到高位遇到的第一个1,抵消后继续与操作检测下一个1,不会做任何多余检测】

方法2的代码:

   public int NumberOf1(int n) {
        int count=0;
        while(n!=0){
            n=n&(n-1);
            count++;
        }
        return count;
    }

2..力扣_求两整数和,不得使用四则运算符号+-*/2

public class Num65_不用加减乘除做加法 {
    //通过有无进位区分不同情况(3+2=011+010=101)
    // 无进位时,和异或操作运算规律相同,1^0=1,0^0=0;有进位时,和与运算规律相同,同时与后结果向前左移了一位,1&1=1
    //数字和=无进位和(两数异或)+进位和(两数相与后左移一位)
    //(a^b) ^ ((a&b)<<1) 即:每次无进位数 + 每次得到的进位数--我们需要不断重复这个过程,直到进位数为0为止;
    public int add(int a, int b) {
        int num=(a&b)<<1;//进位和【带括号,不带会有问题】
        int sum=a^b;//无进位的和
        while (num!=0){
            a=sum;
            b=num;
            sum=a^b;
            num=(a&b)<<1;
        }
        return sum;
    }
}

3.力扣_数组中数字出现的次数

        一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)

方法一:位运算

     /**
     * 分析:两个不一样的数a和b,其他数两两相等。考虑异或操作,相异为真,相同为假
     *      所以将数字全部异或,最后得到的结果一定是a^b。此时思考两者异或后结果x的二进制位
     *      xi=0时说明ab的位相同,异或后才能为0,xi=1说明ab对应位不同,相异或才为真
     *      两个数是不一样,所以a和b的对应位上至少有一个位ai,bi上的数字是不同的(如2和3,010,011,最低位数字不同)
     *      取不同的这个位(xi=1(aibi不同的位)的位),按这个位将数组分为两个数组,将a和b放在不同的数组中,如果该位为0就分到第一组,否则就分到第二组(xi=1则必然有一个是0一个是1,才能异或成xi=1)
     */
    /**
     * 算法
     * 先对所有数字进行一次异或,得到两个出现一次的数字的异或值。
     * 在异或结果中找到任意为1的位(找不为0的最低位即可)。
     * 根据这一位对所有的数字进行分组。
     * 在每个组内进行异或操作,得到两个数字。
     */
    public int[] singleNumbers(int[] nums) {
        int a=0;
        int b=0;
        int x=0;
        int div=1;//找两个数异或后结果的xi=1的位【1&1=1,1&0=0,当xi和div相与结果为1时xi一定为1】
        for (int num :nums) {
            x^=num;
        }
        //此时x=a^b,div循环左移直到xi=1
        while ((x&div)==0){
            div<<=1;
        }
        //此时找到了为1的位:...000div000...
        //按此位分组:该位异或结果是div=1,那么一定一个0一个1,是0一组,1另一组。让每个数与div相与,相与=0说明原该位是0,为1则原该位是1
        for (int num :nums) {
            if((num&div)==0){
                a^=num;//将组内数据异或
            }else {
                b^=num;
            }
        }
        return new int[]{a,b};
    }

方法二:也可以map集合存储数字及其对应次数(存在空间复杂度问题)

 Map<Integer,Integer> map=new HashMap<>();//map集合开辟了新的空间,空间复杂度会不为O(1)
    public int[] singleNumbers(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            if(!map.containsKey(nums[i])){
                map.put(nums[i],1);
            }else {
                map.put(nums[i],map.get(nums[i])+1 );
            }
        }
        int[] res=new int[2];
        int i=0;
        for (Map.Entry<Integer,Integer> entry:map.entrySet()){
            if(entry.getValue()==1){
                res[i]=entry.getKey();
                i++;
            }
        }
        return res;
    }

4.力扣_数组中数字出现的次数

        在一个数组nums中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

/**
 * 分析:一个数字只出现一次之外,其他数字都出现了三次,那么这些出现了三次的数字对应的二进制位上的数字和余上3==0
 *      统计所有数字的各二进制位中1的出现次数,并对3求余,结果则为只出现一次的数字。
 */

/**
 * 算法:
 * 1)将nums中所有数字展开为32位的二进制数,对所有数字的对应位的二进制进行计数(将对应的二进制位相加)
 * 1110+
 * 1110+
 * 1110+
 * 1001(对应二进制位计数相加):
 * 4331
 * 2)对于相加后的结果的对应位:如果找到一个位的位数不能被3整除,说明这个位的数是构成目标数的一部分
 * 3)记录结果的当前二进制位代表的十进制位tmp,结果:循环res+=tmp得到结果
 * tmp=1;(2^0=1)res=0;
 * 1%3=1,res+=tmp=1;temp*=2=2(更新temp到下一个二进制位(每次都需要更新))
 * 3%3=0,不执行res+=temp;(temp*=2)=4
 * 3%3=0,不执行res+=temp;(temp*=2)=8
 * 4%3=1;res+=temp=1+8=9;(temp*=2)=16
 * 再前面位数全是0,0%3=0;res不执行,最后返回res就是只出现一个的数字
 */
public class Num56_II数组中数字出现的次数 {
    public int singleNumber(int[] nums) {
        //nums中所有数字展开成对应二进制,他们对应位的和存储到一个32长度数组中
        int[] byteArray=new int[32];
        for (int num:nums){//遍历每个数字
            for (int i = 0; i < 32; i++) {//每个数字对应的32位
                byteArray[i]+=((num>>i)&1)==1?1:0;//与1相与等于本身(1&0=0,1&1=1)
            }
        }
        //相加后结果对应位byteArray[i]可否被3整除,如果找到一个位的位数不能被3整除,说明这个位的数是构成目标数的一部分,将对应位转化为十进制形式
        //转化方法:temp记录每个位对应的十进制数:2^0;2^1;2^2;2^3...,当不能整除(余数为1)时,res+=temp。最后返回res即可
        int res=0;
        int temp=1;
        for (int i = 0; i < 32; i++) {
            if(byteArray[i]%3==1){
                res+=temp;
            }
            temp*=2;
        }
        return res;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值