您的位置:首页 > 编程语言

编程中 常见的位运算问题

2016-08-26 16:39 218 查看

常见的位运算问题

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

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

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

& 与
| 或
^ 异或

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



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];
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  二进制 位运算