编程中 常见的位运算问题

来源:互联网 发布:考试软件哪个好 编辑:程序博客网 时间:2024/05/18 00:13

常见的位运算问题

二进制数和字符串一样都是编程问题中常遇见的一系列问题 ,许多人觉得很难 ,为什么呢?

因为 ,里面计算可能会用的一些想不到的公式  和 计算思路 。
所以 ,许多的人觉得二进制问题很难实现 。
另外 ,在编程中的许多问题如果使用位运算的方法解决的话,可能会更简单。当然也可能没什么效果哦。


编程中的位运算要用到位操作符  
位操作符包括 


& 与
|  或
^ 异或

接下就由我向大家介绍一些这样的题目(可以使用位运算计算的):


1、两数交换(不允许创建临时变量)


两个数的交换问题,相信的大家都不是很陌生吧  !!

这是一个很简单的问题 ,但是就是有一些变态就是爱给人出难题 ————说不能创建临时变量

看到后是不是很无奈啊;
下面我来讲几种方法吧!!

方法一:


a = a+b;//////a现在等于a 与 b的和
b = a -b;//////b现在等于   (a 与 b的和) -b  为  a最初的值
a = a -b;//////a现在等于   (a 与 b的和) -b  为  b最初的值(因为b的当前值为a的最初值)

void Swap(int *a,int *b){ *a = *a+*b; *b = *a -*b; *a = *a -*b; }
但是这个方法有个缺陷 ,就是两数相加 ,有可能会超出整型值的范围。

方法二:

既然加减法可行 ,当然也可以了

a = a*b;//////a现在等于a 与 b的积
b = a /b;//////b现在等于   (a 与 b的积) /b  为  a最初的值
a = a /b;//////a现在等于   (a 与 b的积) /b  为  b最初的值(因为b的当前值为a的最初值)

<span style="font-size:18px;">void Swap(int *a,int *b){ *a = *a**b; *b = *a /(*b); *a = *a /(*b); }</span>
但是,这个方法也是有缺陷的,就是 除数不能为零

方法三:

就是使用异或的方法
a = a^b;//////a现在等于a 与 b异或的结果
b = a^b;//////b现在等于   (a 与 b异或的结果) ^b  为  a最初的值
a = a^b;//////a现在等于   (a 与 b异或的结果) ^b  为  b最初的值(因为b的当前值为a的最初值)
<span style="font-size:18px;">void Swap(int *a,int *b){ *a = *a^*b; *b = *a ^*b; *a = *a ^*b; }</span>

这就是将平常的问题使用二进制的方法解决。。

2、计算一个整数二进制形式中1的个数

写一个函数返回参数二进制中 1 的个数
比如:
15      0000 1111       4 个 1

这个问题如果用平常的方法一个一个的算的话,写出来的代码会很繁琐 ,同样错误也不少

下面就让哥来介绍一种简单方法吧

要想解决这个问题就要  知道一个公式 
 n  =  n&(n -1);
这个公式只要运行一次 n的二进制式中就少一个 1
例如 :
n  =  15   
1111
n  =  15&14    
1111 & 1110  = 14
n  =  14&13    
1110 & 1101  = 12
n  =  12&11   
1100 & 1011  = 8 
n  =   8 & 7
1000&0111 = 0
运行了四次   所以  15的二进制数中有4个1
int  count_one_bits(int value){       int ret = 0;   while(value)   {value = value &(value-1);ret++;   }   return ret;}

3、计算一个整数二进制形式中0的个数


既然有求二进制数中1的个数  ,就肯定有求 0的个数的 

写一个函数返回参数二进制中 0的个数
比如: 
15      0000 0000 0000 0000 0000 0000 0000 1111       28 个 1


当然,你可以先求出1的个数 ,然后一减就是 0的个数
下面就让哥再来介绍一种简单方法吧

n  =  n|(n + 1);
例子就不用举了 ;和求一的个数差不多
int  count_zero_bits(int value){       int ret = 0;   while(value+1)   {value = value |(value+1);ret++;   }   return ret;}


4、将二进制位模式从左到右翻转后的值

编写函数:
unsigned int  reverse_bit(unsigned int value);
这个函数的返回 值value的二进制位模式从左到右翻转后的值。


例如
在32位机器上25这个值包含下列各位:
00000000000000000000000000011001
翻转后:(2550136832)
10011000000000000000000000000000
程序结果返回:
          2550136832
代码实现:
unsigned int  reverse_bit(unsigned int value){unsigned int ret  = 0 ;int i =  0;int num =  0;for(i = 0;i<32;i++){num =  (value>>i)&1;//求出每一位二进制位的数ret += num* pow(2,31-i);//根据要求重新计算}return ret;}



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

原题目是这样的:

一组数据中只有两个数字出现了一次。
其他所有数字都是成对出现的。请找出这个数字。(使用位运算)



题目中明确表示要使用位运算来解决这个问题;
在这里就要知道位操作符(^     异或)的特点是什么?

异或操作符   :
1、两个相同的数异或后为 0 ;
2、任何一个数与0异或的结果都是这个数。

在这个题目中就要运用到这个特点 ,
要解决这个问题   :就要将数组中的数全部异或  得到的结果 就是只出现一次的两个数异或后的结果 ,
然后,根据这个结果将数组分为两部分,将这两部分的数各自异或后得到的结果就是要找的这两个数...

代码实现
void search_date(int arr[],int len ,int *num1,int *num2)//num1 与 num2为要找的那两个数的地址{int num =  0;int  i = 0 ;int m = 0 ;for(i = 0;i<len;i++){num ^=arr[i];}//求出所有数异或的结果num while(((num>>m)&1)!=1)//找到num二进制式中为1的位{m++;}for(i = 0 ;i<len;i++){if(((arr[i]>>m)&1) ==1)//将数组分为两组*num1 ^=arr[i];//各自异或else*num2 ^=arr[i];}}

























1 0