位运算 总结/题目

不同于算术运算规则,逻辑运算不存在进位与借位的关系,每一位都独立进行,不受其他位的影响。
在这里插入图片描述

异或运算 特点:

还记得位操作中的异或吗?计算规则如下。
0 ⊕ 0 = 0
1 ⊕ 1 = 0
0 ⊕ 1 = 1
1 ⊕ 0 = 1
总结起来就是相同为零,不同为一。

  1. 一个数和0做异或运算等于本身:a⊕0 = a
  2. 一个数和其本身做异或运算等于0:a⊕a = 0
  3. 异或运算满足交换律和结合律:a⊕b⊕a = (a⊕a)⊕b = 0⊕b = b

(1)136. 只出现一次的数字(其余出现2次)

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

示例 1:

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

输入: [4,1,2,1,2]
输出: 4

思路:异或运算

标签:位运算
本题根据题意,线性时间复杂度 O(n),很容易想到使用 Hash 映射来进行计算,遍历一次后结束得到结果,但是在空间复杂度上会达到 O(n),需要使用较多的额外空间
既满足时间复杂度又满足空间复杂度,就要提到位运算中的异或运算 XOR。
时间复杂度:O(n),空间复杂度:O(1)
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int len=nums.size();
        int res=0;
        for(int i=0; i<len; i++){
            res ^=nums[i];
        }
        return res;
    }
};

(2)剑指56.2 数组中唯一中出现一次的数字(137. 只出现一次的数字 II(其余出现3次))

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

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

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

思路:

统计所有数字中每个位中1出现的总数,那么对于某个位,1出现的次数一定是3的倍数或者是1或者是0,那么对这个数%3得到的结果就是目的数字在该位上的值

假如例子是 1 2 6 1 1 2 2 3 3 3, 3 个 1, 3 个 2, 3 个 3,1 个 6
1 0 0 1
2 0 1 0 
6 1 1 0 
1 0 0 1
1 0 0 1
2 0 1 0
2 0 1 0
3 0 1 1  
3 0 1 1
3 0 1 1      
看最右边的一列 1001100111 有 6 个 1
再往前看一列 0110011111 有 7 个 1
再往前看一列 0010000 有 1 个 1
我们只需要把是 3 的倍数的对应列写 0,不是 3 的倍数的对应列写 1    
也就是 1 1 0,也就是 6。

原因的话,其实很容易想明白。如果所有数字都出现了 3 次,
那么每一列的 1 的个数就一定是 3 的倍数。
之所以有的列不是 3 的倍数,就是因为只出现了 1 次的数贡献出了 1。所以所有不是 3 的倍数的列写 1,
其他列写 0 ,就找到了这个出现 1 次的数。

int res = 0; 还必须初始化为0,否则报错!

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        //考虑每一位
        for(int i = 0;i < 32;++i){
            int sum = 0;
            //考虑每个数
            for(int j = 0;j < nums.size();++j){
                sum += (nums[j] >> i) & 1;  //考虑每位上1的个数。如果当前位是1,就+
            }
            res += (sum % 3) << i;  //还原到对应位置  res ^= (sum % 3) << i 也可
        }
        return res;
    }
};

(3)剑指56.1 数组中只出现一次的两个数字(260. 只出现一次的数字 III( 找出只出现一次的那两个元素。))

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例 :

输入: [1,2,1,3,2,5]
输出: [3,5]

思路:异或

现在数组中有两个数字只出现1次,直接异或一次只能得到这两个数字的异或结果,但光从这个结果肯定无法得到这个两个数字。因此基于single number I 的思路——数组只能有一个数字出现1次。

设题目中这两个只出现1次的数字分别为A和B,如果能将A,B分开到二个数组中,那显然符合“异或”解法的关键点了。因此这个题目的关键点就是将A,B分开到二个数组中。由于A,B肯定是不相等的,因此在二进制上必定有一位是不同的。根据这一位是0还是1可以将A,B分开到A组和B组。而这个数组中其它数字要么就属于A组,要么就属于B组。再对A组和B组分别执行“异或”解法就可以得到A,B了。而要判断A,B在哪一位上不相同,只要根据A异或B的结果就可以知道了,这个结果在二进制上为1的位就说明A,B在这一位上是不相同的。其实找到任何一个位置上的1都可以。

比如

int a[] = {1, 1, 3, 5, 2, 2}
整个数组异或的结果为3^5,即 0x0011 ^ 0x0101 = 0x0110

对0x0110,第1位(由低向高,从0开始)就是1。因此整个数组根据第1位是0还是1分成两组。

a[0] =1  0x0001  第一组
a[1] =1  0x0001  第一组
a[2] =3  0x0011  第二组
a[3] =5  0x0101  第一组
a[4] =2  0x0010  第二组
a[5] =2  0x0010  第二组
第一组有{1,1,5},第二组有{3,2,3},然后对这二组分别执行“异或”解法就可以得到5和3了。
// diff &= (~diff) + 1;

    0110 (a)
  & 1010 (~a+1)
    0010 (结果) 
class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        vector<int> result;
		int diff = 0;
		for (int i = 0; i < nums.size(); i++) {
			diff ^= nums[i];
		}
		// diff为 num1 ^ num2, 找出diff的最低位
		diff &= (~diff) + 1;
		int num1 = 0;
		int num2 = 0;
		for (int i = 0; i < nums.size(); i++) {
			if ((diff & nums[i]) == 0) {
				num1 ^= nums[i]; //第一组的所有数异或
			}
			else {
				num2 ^= nums[i]; //第二组的所有数异或
			}
		}
		return { num1, num2}; //返回结果2个数
    }
};
注意2个地方:
1、
	int num1 = 0;
	int num2 = 0;
	异或的初始值必须初始化为0;
2、
	(diff & nums[i]) == 0  注意这里的优先级,记得加括号!		

(4)面试题15. 二进制中1的个数(LC191)

编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'

在这里插入图片描述

思路:把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count=0;
        while(n!=0){
            count++;
            n=n&(n-1);  //1110->1100->1000->0000
        }
        return count; 
    }
};

变换:求有多少个连续的1,,变成左移。。

#include<iostream>
using namespace std;
int main()
{
    int byte;
    while(cin>>byte)
    {
        int k=0;
        while(byte!=0){
            k++;
            byte=byte&(byte<<1);
        }
        cout<<k<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值