位运算相关例题

位运算:&、|、^、~、<<、>>

符号描述运算规则
&两个位都为1时,结果才为1
|两个位都为0时,结果才为0
^异或两个位相同为0,相异为1
~取反0变1,1变0
<<左移各二进位全部左移若干位,高位丢弃,低位补0
>>右移各二进位全部右移若干位。对无符号数,高位补0;有符号数,一般都是补符号位(算术右移)

备注:对于有符号位的右移,有的编译器,会直接补0(逻辑右移)

利用“异或”处理数组的相关算法

异或运算

异或是一种基于二进制的位运算,用符号XOR或者 ^ 表示。运算时对运算符两侧的数转化为二进制形式,同值取0,异值取1。

异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位。

  • 性质
    1、交换律
    2、结合律(即(a^b)^c == a^(b^c))
    3、对于任何数x,都有x^x=0,x^0=x
    4、自反性A ^ B ^ B = A^ 0 = A

寻找数组中丢失的数

  1. 缺失数字
    给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
    示例 1:
    输入: [3,0,1]
    输出: 2
    https://leetcode-cn.com/problems/missing-number/solution/que-shi-shu-zi-by-leetcode/

方法一:排序

方法二:哈希表

方法三:位运算:由于异或运算(XOR)满足结合律,并且对一个数进行两次完全相同的异或运算会得到原来的数,因此我们可以通过异或运算找到缺失的数字。

方法四:数学,等差数列求和,注意防止数据溢出

找出数组中两个只出现一次的数字

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

  • 思路:
    我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了。
    由于这两个数字肯定不一样,那么这个异或结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到第一个为1的位的位置,记为第N位。

现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0。

现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字而其他数字都出现了两次

到此为止,所有的问题我们都已经解决。

代码示例

void FindNumsAppearOnce(int data[], int length, int &num1, int &num2) 
{ 
    if (length < 2) 
       return;
// get num1 ^ num2
  int resultExclusiveOR = 0;
  for (int i = 0; i < length; ++ i)
      resultExclusiveOR ^= data[i];
      
  // get index of the first bit, which is 1 in resultExclusiveOR
  unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR);
  num1 = num2 = 0;
  for (int j = 0; j < length; ++ j)
  {
        // divide the numbers in data into two groups,
        // the indexOf1 bit of numbers in the first group is 1,
        // while in the second group is 0
        if(IsBit1(data[j], indexOf1))
              num1 ^= data[j];
        else
              num2 ^= data[j];
   }
}

unsigned int FindFirstBitIs1(int num) 
{ 
    int indexBit = 0; 
    while (((num & 1) == 0) && (indexBit < 32)) 
    { 
        num = num >> 1; 
        ++ indexBit; 
    }
    return indexBit;
}

bool IsBit1(int num, unsigned int indexBit) 
{ 
    num = num >> indexBit;
    return (num & 1);
}

有N+2个数,N个数出现了偶数次,2个数出现了奇数次(这两个数不相等)

------------------------------------------------------------------------------------------------腾讯笔试题

问用O(1)的空间复杂度,找出这两个数,不需要知道具体位置,只需要知道这两个值
思路:同上
第一步:数组中每个数异或一次,得到2个奇数次的数的异或结果X(偶数次的数异或都为0)。
第二步:两个不同数异或得到的X,一定不为0;我们找出X的二进制数中第一个为1的位置,记为N(这两个不同数的二进制在第N位是不同的),然后根据第N位是否为1,将所有的数分为2组,这时,两个出现奇数次的数一定是分别在其中一组。
第三步:对每组的中每个数异或一次,最后得到的两个数就是结果

n&(n-1)的作用

n&(n-1):消除数字 n 的⼆进制表⽰中的最后⼀个1

例题

1.判断⼀个数是不是 2 的指数
⼀个数如果是 2 的指数, 那么它的⼆进制表⽰⼀定只含有⼀个 1:
在这里插入图片描述

bool isPowerOfTwo(int n){
    if(n<=0)    return false;
    return (n&(n-1))==0;
}
  1. 统计一个二进制数n中1的个数(汉明权重)

关于移位

  1. 计算器存储的都是补码,正数的补码是其原码,负数的补码是由原码变换而来:符号位不变,数值位取反,加1。
  2. 对于正数的移位:左移后在右边补0,若一直左移,最终是0;右移后再左边补0,若一直右移,最终是0
  3. 对于负数的移位:左移和正数一样在右边补0,一个负数再左移过程中有正有负,不用处理符号位,若一直左移,最终是0;负数右移则需要保持为负数,所以右移过程中需要在左边补1,若一直右移,最终会为-1 (二进制全1,就是-1的补码)

利用上面的n&(n-1)消除最低位的1

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

注意这题不要通过右移,记录最低位为1的个数,因为负数右移最终会为-1(二进制全1)
可以用一个flag=1;依次与数n相与,判断是否为0,是,count++;然后flag<<1,再循环上述操作,直到flag最终左移为0。

int NumberOf1(int n){
    int count=0;
    unsigned int flag=1;
    while(flag){
        if(flag&n)
            count++;
        flag=flag<<1;
    }
    return count;
}

判断奇偶数

  • if(n&1==0):偶数
  • if(n&1==1):奇数

判断两个数是否异号

计算机存储的都是补码,
异号的数异或的结果,符号位是1,是负数<0;
同号的数异或的结果,符号位是0,是正数>0;

//判断a,b是否异号
bool func(int a,int b)
{
    return ((a^b)<0);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值