算法模板-----位运算

Posted 栋次大次

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法模板-----位运算相关的知识,希望对你有一定的参考价值。

位运算符

single-number
single-number-ii
single-number-iii
number-of-1-bits
counting-bits
reverse-bits
bitwise-and-of-numbers-range

位运算符

运算符描述规则
&两个位都是1结果为1
|两个位都是0结果为0
^异或相同为0,相异为1
~取反原位取反
<<左移二进制各位全部左移若干位,高位丢弃,低位补0
>>右移二进制各位全部右移若干位,对无符号数,高位补0;有符号数,各编译器处理方法不一样,有补符号位(算术右移),有补0(逻辑右移)

常见操作

a=0^a=a^0
0=a^a
上式推出:a=a^b^b

交换两个数,不必使用临时变量
a=a^b
b=a^b
a=a^b

移除最后一个1
a=n&(n-1)
获取最后一个1
diff=(n&(n-1))^n
或者
diff=x&(-x) # -x=~x+1

常见题目

single-number

给定一个非空整数数组,除了某个元素只出现一次外,其他每个元素均出现两次
思路:利用异或

int singleNumber(vector<int>& nums)
	// 两个相同的数异或为0  相异为本身
	int res = 0;
	for(auto n : nums)
		res = n ^ res;
	
	return res;

single-number-ii

给定一个非空数组,除了某个元素只出现一次外,其余每个元素均出现三次。
思路:对于出现三次的数字,各二进制出现的次数都是三的倍数。因此,统计所有数字的各二进制位中1出现的次数,并对3求余,结果则为只出现一次的数字。

//位状态机。推荐使用这种方法,很巧妙。
int singleNumber(vector<int>& nums)
	int a = 0, b = 0;
	for(int num : nums)
		a = (a ^ num) & (~b);
		b = (b ^ num) & (~a);
	
	return a;


// 解法二,遍历统计每位
int singleNumber(vector<int>& nums)
	int res = 0;
	for(int i = 0; i < 32; i++)
		int cnt = 0;
		for(auto x : nums)
			cnt += (x>>i)&1;
		
		res |= (cnt%3)<<i;
	
	return res;

single-number-iii

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

vector<int> singleNumber(vector<int>& nums)
	int s = 0;
	for(auto n : nums)
		// 去除出现两次的数
		s = s ^ n;
	
	s = s & (-s); // 保留最右边的1
	vector<int> res(2, 0);
	for(auto n : nums)
		if(n & s)
			res[0] ^= n;
		else
			res[1] ^= n;
		
	
	return res;

number-of-1-bits

输入一个无符号整数,返回其二进制表达式中为1的个数(汉明重量)

int hammingWeight(uint32_t n)
	int count = 0;
	while(n > 0)
		n = n & (n-1); //移除最后一个1
		count++;
	
	return count;

counting-bits

给定一个非负整数num。对于0<=i<=num范围内中的每个数字i,计算其二进制中的1的数目并将它们作为数组返回。

vector<int> countBits(int num)
	vector<int> res;
	for(int i = 0; i <= num; i++)
		res.push_back(hammingWeight(i));
	


int hammingWeight(int num)
	int count = 0;
	while(num != 0)
		num &= (num - 1);
		count++;
	
	return count;

另一种动态规划解法:让上一个缺1的元素+1即可

vector<int> countBits(int num)
	vector<int> res(num + 1);
	for(int i = 1; i <= num; i++)
		res[i] = res[i & (i-1)] + 1;
	
	return res;

reverse-bits

颠倒给定的32位无符号整数的二进制位

uint32_t reverseBits(uint32_t n)
	uint32_t res = 0;
	int pow = 31; //记录位置
    while(n != 0)
    	//取出最后一位
    	res += (n & 1) << pow;
    	n >>= 1;
    	pow--;
    
    return res;

bitwise-and-of-numbers-range

给定范围[m,n], 其中0<=m<=n<=2147483647,返回此范围内所有数字的按位与(包含m、n)
思路:1. 迭代进行与操作(可能会超时)2. 寻找公共前缀

//解法1
int rangeBitwiseAnd(int m, int n)
	while(m < n)
		n &= n -1;
	
	return m & n;


// 解法二
int rangeBitwiseAnd(int m, int n)
	int count = 0;
	while(m != n)
		m >>= 1;
		n >>= 1;
		count++;
	
	return n <<= count;

以上是关于算法模板-----位运算的主要内容,如果未能解决你的问题,请参考以下文章

位运算 - 异或

位运算基础(Uva 1590,Uva 509题解)

第二部分 位运算符赋值运算符三元及一元运算符和语句分类

n皇后(位运算)

位运算符-异或^

算法之美