位运算

来源:互联网 发布:淘宝女用催情药 编辑:程序博客网 时间:2024/06/06 19:51

位运算
基础知识:
操作数可以是任意的整数类型 (或者char类型),并且没有副作用。为了可移植性,最好仅对无符号数进行位移操作.

1. 一.位移运算符

左移运算
左移n位就是乘以2的n次方。
在防止溢出的前提下,丢弃最高位,0补最低位。
但有符号数不完全实用,因为左移后可能导致符号变化。
2右移运算
右移运算符“>>”是双目运算符。右移n位就是除以2的n次方。
对于有符号数,符号位向右移动后,正数补0,负数由编译器决定。并且符号位将同时移动。
当正数时,最高位为0. 当负数时,符号位为1。(符号永远不变!)
3右移对符号位的处理和左移不同:
当左移的位运算超过该数值类型最大位数时,编译器会用左移位数去模类型的最大位数,然后按余数进行移位。
在C中,左移是逻辑/算术左移(两者完全相同),右移是算术右移,会保持符号位不变.实际应用中可以根据情况用左/右移做快速的乘/除运算,这样会比循环效率高很多. 而且,
★只要是乘法或除法,以一个整数均可以用位移的方法得到结果
a=a*18; 等价于 a=(a<<4)+(a<<1);
以下n可以是某个数的一位,或者一个数。
一. 按位或运算 |
1 | 1=1
1|0=0
0|0=0
n | 1=1 n|0=n
位的设置: a |=(1<<j)

二按位与运算 &
1&1=1
1&0=0
0&0=0
n&1=n(获取)n&0=0(清零)n&m=相同的位(找出两个数相同的位)
1.清零 a&0=0; a&=~(1<<j);
2.获取 a&=(1<<j);
★3.判断int型变量a是奇数还是偶数
a&1 = 0 偶数
a&1 = 1 奇数
三按位异或运算 ^
1^1=0
1^0=1
0^0=0
n^1=~n n^0=n n^m=不同的位(找出两个数不同的位)
1. 使特定位翻转 要使哪几位翻转就将与其进行∧运算的该几位置为1即可。
2 与0相∧,保留原值.
3.由 1 2两点得到 可以判断出 一些数中唯一的出现次数为奇数的那个数。对于其他 高级应用。
★4.交换两个值,不用临时变量.
我们可以在不用引入其他变量就可以实现变量值的交换,并且杜绝了溢出的弊端。
a = a^b;
b = a^b;
a = a^b;
四 按位反
~n+1=(-n);
四位的设置 测试 清除
位的设置 a|=(1<<j)
位的测试(a&(1<<j))
位的清除 a&=~(1<<j)
五 位域
修改位域 a=a&~0x0070|j<<4
获取位域 a=(i>>4)&0x0070
2. ★高级应用:

1.int型变量循环左移k次,即a=a<<k|a>>16-k (设sizeof(int)=16)
2. int型变量a循环右移k次,即a=a>>k|a<<16-k (设sizeof(int)=16)
3 整数的平均值
对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,

(x&y)+((x^y)>>1);

4. 判断一个整数是不是2的幂,对于一个数 x >= 0,判断他是不是2的幂

return ((x&(x-1))==0)&&(x!=0);

5计算绝对值
int abs( int x )
{
int y ;
y = x >> 31 ;
return (x^y)-y ; //or: (x+y)^y
}

6 取模运算转化成位运算 (在不产生溢出的情况下)
a % (2^n) 等价于 a & (2^n - 1)
a % 2 等价于 a & 1 ( a & log2(2))
a % 4 等价于 a & 2 ( a & log2(4))
.....
a % 32 等价于 a & 5

7 if (x == a) x= b;
   else x= a;
等价于 x= a ^ b ^ x;

8判断 1的个数
1. int count1Bits(long n)
2. {
3. int count =0;
4. while(n)
5. {
6. count++;
7. ★ n&=(n-1);
8. }
9. return count;
10. }
看起来似乎采用位运算的代码比朴素方法代码要复杂的多,但是在性能上有着朴素方法无法比拟的优越性,只要四步简单的运算就能达到目的,而朴素方法不是用循环就是递归,这大大降低了CPU的运算性能。
int CountOne(unsigned long n)
{
//0xAAAAAAAA,0x55555555分别是以“1位”为单位提取奇偶位
n = ((n & 0xAAAAAAAA) >> 1) + (n & 0x55555555);
//0xCCCCCCCC,0x33333333分别是以“2位”为单位提取奇偶位
n = ((n & 0xCCCCCCCC) >> 2) + (n & 0x33333333);
//0xF0F0F0F0,0x0F0F0F0F分别是以“4位”为单位提取奇偶位
n = ((n & 0xF0F0F0F0) >> 4) + (n & 0x0F0F0F0F);
//0xFF00FF00,0x00FF00FF分别是以“8位”为单位提取奇偶位
n = ((n & 0xFF00FF00) >> 8) + (n & 0x00FF00FF);
//0xFFFF0000,0x0000FFFF分别是以“16位”为单位提取奇偶位
n = ((n & 0xFFFF0000) >> 16) + (n & 0x0000FFFF);

return n;
}

9、子集
  枚举出一个集合的子集。设原集合为mask,则下面的代码就可以列出它的所有子集:

  for (i = mask ; i ; i = (i - 1) & mask) ;

10 输出二进制
Scanf(“%d”,&n);
int i=32;
while(i--)
{
printf("%d",(n&(1<<i))>>i);

}
- 一组整数,除了一个只出现一次以外,其他每个整数都恰好出现三次,要寻找那个
处理 一个数位 一个 其他数为三个的情况 ,对于其他方案 用12题方法
public int singleNumber4(int A[]) {
int one = 0; int accumulation = 0; // 出现一次的标志位 // 积累标志位
for (int i = 0; i < A.length; i++)
{
accumulation |= A[i] & one; // 只要第二次或者以上出现,就为1
one ^= A[i]; // 出现奇数次保留,偶数次抛弃
int t = one & accumulation; // 第三次的时候one和accumulation都保留了该位
one &= ~t; // 清零出现三次的该位的值
accumulation &= ~t;
}
return one; }
12.一组整数,除了一个只出现两次以外,其他每个整数都恰好出现四次,要寻找那个数
处理 一个数位 偶数个 其他数为偶数的情况
复杂度为O(32*n)
- # include <stdio.h>
# include<stdlib.h>

int found_number ( long long int a[],int n)
{
int b[64];
int j,i,num=0;
for(j=0;j<32;j++)
{
b[j]=0;
for(i=1;i<=n;i++)
{
b[j]+=((a[i]>>j)&1);
}
}
for(j=0;j<32;j++)
num|=((b[j]%4)/2)<<j;
return num;
}
int main (void)
{
int n;
scanf("%d",&n);
long long int a[n];
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
printf("%d",found_number (a,n));
return 0;
}
13一组整数,除了一个只出现奇数次以外,其他每个整数都恰好出现偶数次,要寻找那个
处理 一个数位 奇数个 其他数为偶数的情况

- # include<stdio.h>

int main (void)
{ int a=0;
int input;
while (~scanf("%d",&input))
{
a^=input;
}
printf("%d",a);
return 0;
}

1. 常用的二进制数:

二进制数 二进制值 用处
0xAAAAAAAA 10101010101010101010101010101010 偶数位为1,以1位为单位提取奇位
0x55555555 01010101010101010101010101010101 奇数位为1,以1位为单位提取偶位
0xCCCCCCCC 11001100110011001100110011001100 以“2位”为单位提取奇位
0x33333333 00110011001100110011001100110011 以“2位”为单位提取偶位
0xF0F0F0F0 11110000111100001111000011110000 以“8位”为单位提取奇位
0x0F0F0F0F 00001111000011110000111100001111 以“8位”为单位提取偶位
0xFFFF0000 11111111111111110000000000000000 以“16位”为单位提取奇位
0x0000FFFF 00000000000000001111111111111111 以“16位”为单位提取偶位

1. 运算符优先级

  结合性
() [] -> . ++(后缀自增) --(后缀自减) left to right
! ~ ++(前缀自增) --(前缀自减) + - * sizeof(type) right to left
* / %   
  
  
left
to
right
+ -
<< >>
< <= > >=
== !=
&
^
|
&&
||
? : (条件运算) right to left
= += -= *= /= %= &= ^= |= <<= >>=

















0 0
原创粉丝点击