位运算常见用法小结
#位运算基本操作符:
|或
&与
^异或
~取反
12.26所学:
1.位运算求绝对值abs()
原理阐述:对于一个正数而言,返回自己即可,对于负数,二进制表示为补码形式,补码形式又是等于反码形式加一
00000…0011表示3
11111…1100+1 表示-3
即1111…1101 所以负数取反后加一即可
int abs(int a)
{
return (a<0? (~a+1):a);
}
2.位运算交换两值
//位运算交换两个数值
void swap(int& a,int& b)
{
a=a^b;
b=a^b;
a=a^b;
}
这个我直接记忆"aba等",一句话的事儿(原理不懂的话举几个栗子,就明白了).
3.位运算判断奇数偶数
规律:当一个数为奇数时,他的最后一位总是1(自己想想是不是这样的?),当它是偶数时,他的最后一位是0,那么我们通过将这个数字与1做与运算来判断他到底是奇_偶数?
//位运算判断奇数偶数,这里假设奇数返回为true,偶数返回false
bool ji_ou(int a)
{
return (a&1==1?true:false) ;
}
4.找出唯一成对的数
Problem:
1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次。设计一个算法,将它找出来,你能否设计一个算法实现?
思路一
桶排序思想,开数组,空间为1001,依次填入,检验元素个数,当元素个数为二时返回
思路二(位运算精髓)
#include<cstdio>
#include<stdlib.h>
using namespace std;
int main()
{
int result;
int N=11;
int *given;
given=(int*)malloc(sizeof(int)*N);
for(int i=0;i<10;++i)
{
scanf("%d",&given[i]); /*这十个数中有且只有一个数字是重复的,再次和0~10做异或,偶数次异或的结果做异或运算还是异或,奇数次结果还是它本身所以,最后temp就是重复的那个数字*/
}
int* cur;
cur=(int*)malloc(N*sizeof(int)); /*注意这里不可以在定义的时候给它空间分配,先定义指针再分配空间*/
for(int i=0;i<10;++i)
{
cur[i]=i;
}
for(int i=0;i<10;++i)
{
result^=cur[i]^given[i]; //异或满足交换律,所以可以这样写
}
free( cur);
free( given);
printf("%d",result);
return 0;
}
核心思想:我们利用偶数个同一个数a的异或等于0这一思路,开创一个新的数组分别存0~1000,再让reusult异或两个数组里的元素,奇数个异或结果就是自己,So!最后异或剩下的就是那个成对的数.
5.将整数的奇偶位互换(这里假设从左往右读1,2,3,…位)
#include<stdio.h>
/*用0来做与运算意思是消除,用1来做与运算意识是保留*/
void transfer(int a)
{
int b=a&0xaaaaaaaa; /*取得奇数位操作!!!*/
int c=a&0x55555555; /*取得偶数位操作!!!*/
printf("%d",(b>>1)^(c<<1));
}
int main()
{
int a;
scanf("%d",&a);
transfer(a);
return 0;
}
核心思路:正如代码中写的那样,用0来做与运算表示消除,用1来做与运算表示保留,我们可以通过与0101010101010…01
来做与运算来消除奇数位上的数,通过与10101010101…10做与运算来消除偶数上的数.
我们把第一个运算的结果左移,将第二个运算的结果做右移运算,再将两个左/右移的结果做耦合,**如何耦合?**规律:
a^0=a;
我们利用这点将左移,右移的结果直接做异或运算即可得到正确的答案!!!
6.二进制中一的个数
问题描述:给你一个十进制数字,输出二进制表示中1的个数.
#include<stdio.h>
bool ji_ou(int a)
{
return (a&1==1? true : false);
}
int count_one(int a)
{
int count=0;
while(a!=0)
{
if(ji_ou(a))
{
count++;
}
a>>=1;
}
return count;
}
int main()
{
int count=0;
int a;
scanf("%d",&a);
count=count_one(a);
printf("%d",count);
}
思路讲解:我们通过判断最后一位与1做与&运算的结果来判断 最后一位是不是1,再依次右移,得到结果
7.出现K次和出现一次
问题描述:数组中只有一个数只出现1次,其它数都出现K次,将出现1次的数输出。
#include
using namespace std;
/数组中只有一个数出现了一次,其他的数都出现了K次,请输出那个只出现了一次的数/
//暴力解法:桶排序思想即可
核心思路(位运算)
技巧:K个数字转化为K进制数字做异或运算,结果为0
eg:K=3
101
101
101结果为
000
因为是不进位加法,(1+1+1)%3=0
是不是很巧妙?!
/*
使用位运算时,K分两种情况:
如果K是偶数,非常简单,将所有元素进行异或运算,得到的就是结果。
如果K是奇数,此法不再适用。
这里要用到一个数学规律,一个K进制数,如果不进位加K次,结果一定为0.
如0b111+0b111=0b000(进位舍弃)。
回到题目,如果K是奇数,我们可以把每一个元素都转换成K进制数。把转换出来的每一位都回到对应的结果数组中。最后再把结果数组转换回十进制数。
当然如果k是偶数的话,也可以使用第2种方法,也就是说第2种方法是通用的。
*/
#include<stdio.h>
#include<math.h>
int main(){
int num,k; //num是多少个数 k是出现k次
scanf("%d%d",&num,&k); //num应该满足 (num-1)%k==0
int a[num];
int b[32] = {0}; //用于存 不进位 k进制 的每位数
int i,j;
for(i = 0;i < num;i++){
scanf("%d",&a[i]);
}
for(i = 0;i < num;i++){
int j = 0;
while(a[i]!=0){
b[j] = b[j] + a[i] % k; //这个位本来的加上下一个数%k的余数
b[j] = b[j] % k; //如果达到K则进行不进位的取余
a[i] = a[i] / k;
j++;
}
}
int result;
for(i = 0;i < 32;i++){
result = result + b[i] * pow(k,i);//将k进制不进位的数转为10进制
}
printf("%d",result);
return 0;
}
这道题其实很不容易想到,这个代码如果没懂前会觉得很晦涩难懂,这里的b[j]相当于做每列的加法,依次从最右边的位数开始向下累加,同时对K取余
eg:
101
101
101
最右边开始b[1]=1,b[2]=0,b[3]=1,然后当前a[i]==0
这里可能会有朋友会问到,万一不是同样的元素相邻出现呢?不会有问题的吗?
问得好!我一开始也是这样怀疑的,直到给了几组测试数据后我才渐渐明白基本原理,这里严格的数学证明就不给了(蒟蒻高数太菜)
异或的可交换性.a^ b ^c = a^ c^ b
8.快速幂运算
求a的k次方
int pow(int a,int k)
{
int ans=1;
while(k){
if(k&1)==1) ans*=a;
a*=a;
k>>=1;
}
return ans;
}
本文总结了位运算的各种常见用途,包括求绝对值、数值交换、判断奇偶数、找出数组中唯一重复的数、奇偶位互换、计算二进制中1的个数以及解决出现K次和出现一次的问题。通过实例和技巧分析,揭示了位运算在算法中的高效应用。

2416

被折叠的 条评论
为什么被折叠?



