一、位运算的奇巧淫技

位运算相关基础

  • 在处理整形数值时,可以直接对组成整形数值的各个位进行操作。这意味着可以使用屏蔽技术获得整数中的各个位

  • &(与)、|(或)、^(异或)、~(非)
    与:都为1结果与1,或:有一个为1结果为1,异或:二者不同时结果为1

  • >>和<<运算符将二进制位进行右移或者左移操作

  • >>>运算符将用0填充高位;>>运算符用符号位填充高位,没有<<<运算符(Java)

  • 对于int型,1<<35与1<<3是相同的(因为int型只有32位,超过32位的移动会按32位模截取,Java),而左边的操作数是long型时需对右侧操作数模64

应用

  • 判断奇偶数
    (x&1=1为奇,x&1=0为偶)
  • 获取二进制位是1还是0
    (通过跟左移后的1进行与运算看结果为1还是0)
  • 交换两个整数变量的值
    ( 通过三次两两异或即可)
  • 不用判断语句,求整数的绝对值

补充:异或,可以理解为不进位加法:1+1=0,0+0=0,1+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:找出唯一成对的数

1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不同辅助存储空间,能否设计一个算法实现?

解题思路:
由上述性质3可知,任何数与自身异或为0,因此可以通过将这些数全部与自身异或一遍,结果即为那个奇数个的重复数。

代码:

#include<bits/stdc++.h>
#define random(x)(rand()%x)
using namespace std;
int main()
{
	int N=1001;
	int arr[N];
	for(int i=0;i<N-1;i++)
	{
		arr[i]=i+1;
	}
	//随机选取一个数作为重复数
	srand(int(time(0)));
	arr[N-1]=(random(N-1))+1;
	cout<<arr[N-1]<<endl;
	
	int x=0;
	for(int i=0;i<N;i++){
		x=(x^arr[i])^i; 
	}
	cout<<x<<endl;
	return 0;
}

题2:二进制中1的个数

请实现一个函数,输入一个整数,输出该数二进制表示中1的个数例:9的二进制表示为1001,有2位是1

解题思路:
法1:利用上述“应用”中的第二例,通过该数与1的移位进行相与,判断是否为1,则count加1
法2:通过计算1001变位0需要消除几次1即可得到1的个数,
每次将该数减1,即可消除最低位的1,再与原数相与,直至相与结果为0

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	//法一: 
	int count = 0;
	int n;
	cin>>n;
	char str[32];//显示该数的二进制以验证 
	itoa(n,str,2);
	cout<<str<<endl;
	for(int i=0;i<32;i++){
		if((n&(1<<i))==(1<<i)){
			count++;
		}
	}
	cout<<"法1的结果:"<<count<<endl;

	//法2 
	count=0;
	while(n!=0){
		//每做一次与运算,消除最低位的1 
		n=(n-1)&n;
		count++;
	} 
	cout<<"法2的结果:"<<count<<endl;

	return 0;
}

题3:将整数的奇偶位互换
解题思路:
将整数当成二进制,分别与101010…相与即可保留奇数部分a,与010101…相与即可保留偶数部分b,再将a右移1位,b左移1位,最后将a、b异或

代码:

#include<bits/stdc++.h>
using namespace std;
int m(int i){
	int a = i&0xaaaaaaaa; //1010 1010 1010 ...奇数位 
	int b = i&0x55555555; //0101 0101 0101 ...偶数位 
	return (a>>1)^(b<<1);
} 
int main()
{
	int n;
	cin>>n;
	cout<<m(n)<<endl; 
	return 0;
}

题4:出现k次与出现1次

数组中只有一个数出现了1次,其他的数都出现了k次,请输出只出现了1次的数

解题思路:
两个相同的二进制数做不进位加法,结果为0
10个相同的10进制数做不进位加法,结果为0
k个相同的k进制数做不进位加法,结果为0
可以将这些数都转化为k进制进行不进位加法即可将出现了k次的数都变为0,而最终结果即为出现1次数的k进制再转化为10进制即可

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	//在一组数中,只有一个数出现一次,其他数出现k次,求出这个数
	//这里假设k=3 
	int arr[13]={2,2,2,9,7,7,7,3,3,3,6,6,6};
	int len=13; 
	int k = 3; 
	//存储二进制数中最大的长度 
	int max_len=0;
	int arr2[13][5]={0}; 
	//将每个数字的二进制保存到二维数组中 
	for(int i=0;i<len;i++){
		int temp=arr[i];
		int j=0;
		while(temp){
			arr2[i][j]=temp%k;
			temp/=k;
			j++;
		}
		if(j>max_len){
			max_len=j;
		}
	}
	//定义接收k进制的结果数组并初始化
	int *resArr=new int[max_len];
	for (int i = 0; i < max_len; i ++){
		resArr[i] = 0;
	}
	//将所有二进制数进行不进位加法
	for(int i=0;i<len;i++){
		for(int j=0;j<max_len;j++){
			resArr[j]+=arr2[i][j];
		}
	} 
	
	//将k进制数转换为10进制 
	int res=0;
	for(int j=0;j<max_len;j++){
		res+=(resArr[j]%k)*pow(k,j);
	}
	cout<<res;
	return 0;
} 

PS:出现的小知识点总结:

  • 随机数通过定义#define random(x)(rand()%x),并且要设置种子保证每次随机数不同srand(int(time(0))),通过random(N)可以获得[0,N)之间的随机数
    要获取0-1的浮点数:rand()/(double)(RAND_MAX);
    若要0~10的浮点数,则修改为rand() /(double)(RAND_MAX/10);
    若要0~100浮点数,则修改为rand() /(double)(RAND_MAX/100)
  • 通过itoa(int,str,int)函数可以获得想要的进制串,第一个参数为需要转换的整数,第二个参数为接收转换后的字符数组如定义为char str[32],第三个为需要转换的进制数如2表示转换为2进制
发布了25 篇原创文章 · 获赞 0 · 访问量 1504

猜你喜欢

转载自blog.csdn.net/u014681799/article/details/86766980
今日推荐