位操作符
-
与运算
& 与运算 两个位都是 1 时,结果才为 1,否则为 0,如
1 0 0 1 1
& 1 1 0 0 1------------------------------
1 0 0 0 1
-
或运算
| 或运算 两个位都是 0 时,结果才为 0,否则为 1,如
1 0 0 1 1
| 1 1 0 0 1------------------------------
1 1 0 1 1
-
非运算
~ 取反运算,0 则变为 1,1 则变为 0,如
~ 1 0 0 1 1-----------------------------
0 1 1 0 0
-
异或运算
^ 异或运算,两个位相同则为 0,不同则为 1,如
1 0 0 1 1
^ 1 1 0 0 1-----------------------------
0 1 0 1 0
-
<< 左移运算
<< 左移运算,向左进行移位操作,高位丢弃,低位补 0
int a = 8;
a << 3;
移位前:0000 0000 0000 0000 0000 0000 0000 1000
移位后:0000 0000 0000 0000 0000 0000 0100 0000
-
>> 右移运算
>> 右移运算,向右进行移位操作,对无符号数,高位补 0,对于有符号数,高位补符号位
unsigned int a = 8; a >> 3;
移位前:0000 0000 0000 0000 0000 0000 0000 1000
移位后:0000 0000 0000 0000 0000 0000 0000 0001
int a = -8; a >> 3;
移位前:1111 1111 1111 1111 1111 1111 1111 1000
移位前:1111 1111 1111 1111 1111 1111 1111 1111
-
>>> 无符号右移运算
>>> 运算符将用0填充高位;>> 运算符用符号位填充高位,没有<<< 运算符
常见的位运算问题
-
位操作交换两数
利用异或运算
第一步:a ^= b ---> a = (a^b);
第二步:b ^= a ---> b = b^(a^b) ---> b = (b^b)^a = a
第三步:a ^= b ---> a = (a^b)^a = (a^a)^b = b
//普通操作
void swap(int &a, int &b) {
a = a + b;
b = a - b;
a = a - b;
}
void swap(int &a, int &b){
int t;
t = a;
a = b;
b = t;
}
//位与操作
void swap(int &a, int &b) {
a =a ^ b;
b =a ^ b;
a =a ^ b;
}
-
判断奇偶数
只要根据数的最后一位是 0 还是 1 来决定即可,为 0 就是偶数,为 1 就是奇数。即原数据和1进行与运算来判断最后一位是 0 还是 1.
import java.util.Scanner;
public class t7 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
if((a & 1) == 1) System.out.println("奇数");
else System.out.println("偶数");
}
}
-
位操作交换符号
交换符号将正数变成负数,负数变成正数
整数取反加1,正好变成其对应的负数(补码表示);负数取反加一,则变为其原码,即正数
import java.util.Scanner;
public class t7 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
System.out.println(~a + 1);
}
}
-
求绝对值
整数的绝对值是其本身,负数的绝对值正好可以对其进行取反加一求得,即我们首先判断其符号位(整数右移 31 位得到 0,负数右移 31 位得到 -1,即 0xffffffff),然后根据符号进行相应的操作
int abs(int a) {
int i = a >> 31;
return i == 0 ? a : (~a + 1);
}
-
进行高低位交换
给定一个 16 位的无符号整数,将其高 8 位与低 8 位进行交换,求出交换后的值
34520的二进制表示: 10000110 11011000 将其高8位与低8位进行交换,得到一个新的二进制数: 11011000 10000110
只要将无符号数 a>>8 即可得到其高 8 位移到低 8 位,高位补 0;将 a<<8 即可将 低 8 位移到高 8 位,低 8 位补 0,然后将 a>>8 和 a<<8 进行或操作既可求得交换后的结果。
import java.util.Scanner;
public class t7 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
System.out.println(Integer.toString(a, 2));
System.out.println(Integer.toString((a >> 8) | (a << 8), 2));
}
}
-
位操作统计二进制中 1 的个数
方法一:
1左移i位后和原数据进行与运算,若结果等于1左移i位后的值,则该位数是1
int cnt1 = 0; for (int i = 0;i < 32;i++){//二进制最高32位 if((n&(1<<i))==(1<<i)){//1左移i位与原数据进行与运算,若等于1左移i位的数,则第i位是1 cnt1++; } }
方法二:
原数据右移i位后和1进行与运算并且高位用0补位,若结果为1,则该位是1
int cnt2 = 0; for (int i = 0;i < 32;i++){ if(((n >>> i) & 1) == 1){//原数据右移i位,并高位用0补位,和1进行与运算,结果与1比较 cnt2++; } }
方法三:
(原数据-1)& 原数据 此操作消去目前最低位的1,直至原数据变为零时,所有的1统计完毕
例:11001010
第一次消去结果11001000
第二次消去结果11000000
第三次消去结果10000000
第四次消去结果00000000
int cnt3 = 0; while(n!=0){//原数据变为0,结束条件 n = (n - 1) & n;//原数据-1,和原数据进行与运算,消去最低位的1 消去最低位的1 cnt3++; }
-
判断是否为2的次幂
2的次幂的特点
1
10
100
必有一个1,利用异或判断1的个数是否为1即可
-
3的n次方
例:求3的13次方 n = 13,二进制表示为1101,
那么 3 的 13 次方可以拆解为: 3^1101 = 3^0001 * 3^0100 * 3^1000。
可以通过 & 1和 >>1 来逐位读取 1101,该位为1时将该位代表的乘数累乘到最终结果。
& 1 用来判断该位是否为1
>>1 移位,逐位读取
import java.util.Scanner;
public class t7 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int sum = 1;
int ts = 3;
while(n!=0){
if((n & 1) ==1){
sum = sum * ts;//判断位数为1时,累乘到最终结果
}
ts = ts * ts;
n = n >> 1;//移位
}
System.out.println(sum);
}
}
-
找出不大于N的最大的2的幂指数
例如 N = 19,那么转换成二进制就是 00010011,那么我们要找的数就是,把二进制中最左边的 1 保留,后面的 1 全部变为 0。即我们的目标数是 00010000。
1、找到最左边的 1,然后把它右边的所有 0 变成 1
2、把得到的数值加 1,可以得到 00100000即 00011111 + 1 = 00100000。
3、把 得到的 00100000 向右移动一位,即可得到 00010000,即 00100000 >> 1 = 00010000。
n |= n >> 1; n |= n >> 2; n |= n >> 4;
通过把 n 右移并且做或运算,可以实现把最左边的1的右边所有的0变成1的操作
int findN(int n){ n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8 // 整型一般是 32 位,上面我是假设 8 位。 return (n + 1) >> 1; }
-
找出重复的数
1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次,每个数组元素只能访问一次,设计一个算法,将他找出来。
主要利用异或运算,A ^ A = 0 , A ^ 0 = 1
先构造一个1^2^3^4...^1000 ,与原数据异或运算,则可以消去不重复的元素值,留下一个重复的值
import java.util.Random; public class t1 { public static void main(String[] args) { int N = 1000; int []arr = new int[N]; for(int i=0;i<N-1;i++){ arr[i]=i+1; } arr[N-1] = new Random().nextInt(N-1);//在末尾产生一个随机数 int index = new Random().nextInt(N-1); swap(arr,index,arr.length-1);//将随机数随即放入数组内,构成题目所需要求 for (int i=0;i < N;i++){ System.out.println(arr[i]); } int x1=0; for (int i = 0;i<N-1;i++){ x1=x1^(i+1); }//构造1^2^3^4^5^6.... for (int i=0;i<N;i++){ x1=x1^arr[i];//与数组异或起来,最终只剩重复数字 } System.out.println(x1); } public static void swap(int []arr,int index,int n){ int t; t=arr[index]; arr[index]=arr[n]; arr[n]=t; } }
-
找出落单的数
一个数组除了某一个数字之外,其他的数字都出现了两次。请找出这个数
异或运算,将数组中每一个元素进行异或运算,消去出现两次的数字,留下落单的数字
import java.util.Random;
public class t2 {
public static void main(String[] args) {
int N = 11;
int []arr = new int[N];
for(int i=0;i<N/2;i++){
arr[i]=arr[N/2+i]=i+1;
}
arr[N-1] = new Random().nextInt(100);//在末尾产生一个随机数
for (int i=0;i < N;i++){
System.out.println(arr[i]);
}
int x1=0;
for (int k=0;k<N;k++){
x1=x1^arr[k];//与数组异或起来,成对的全部消掉,只留下单个的
}
System.out.println(x1);
}
}
-
奇偶位互换
将一个整数的奇偶位互换
例:9的二进制表示位1001 奇偶互换0110 对应十进制为6
方法:
原数据和0xaaaaaaaa(1010 1010 1010 ...)进行与运算,取出原数据的偶数位
原数据和0x55555555(0101 0101 0101 ...)进行与运算,取出原数据的奇数位
将偶数位右移一位,奇数位左移一位,然后进行异或运算
import java.util.Scanner;
public class t5 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println(Integer.toString(n,2));
int ou = 0xaaaaaaaa & n; //(1010 1010 1010 ... ) & n 此项和原数据与运算,取出原数据的偶数位
int ji = 0x55555555 & n; //(0101 0101 0101 ... ) & n 此项和原数据与运算,取出原数据的奇数位
System.out.println(Integer.toString(ou,2));
System.out.println(Integer.toString(ji,2));
int ou1 = ou >> 1;//偶数位右移一位
int ji1 = ji << 1;//奇数位左移一位
System.out.println(Integer.toString(ou1 ^ ji1,2));
System.out.println(ou1 ^ ji1);//异或运算
}
}
-
出现K次与出现1次
数组中只有一个数出现了1次,其他的数都出现了K次,请输出只出现了1次的数
K个相同的k进制数做不进位加法,结果为0
public static void main(String[] args) {
int []nums={2,2,2,1,1,1,2,1,4,5,5,5,5};
int num=nums[0];
for (int i=1; i<nums.length; i++) {
num=num^nums[i];
}
System.out.println(num);
}
-
进制的转化
1.手工取余法
2. Integer.toString(n,)
import java.util.Scanner;
public class t6 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n1 = sc.nextInt();
float m = sc.nextFloat();
zheng(n1);
System.out.println();
fu(m);
}
public static void zheng(int n){
while(n != 0){
System.out.print(n % 2);//整数取余
n/=2;
}
}
public static void fu(float m){
StringBuilder sb = new StringBuilder("0.");//字符串拼接
//乘2挪整
while (m > 0){
float f = m * 2;
if(f >= 1){
sb.append("1");
m = f - 1;
}
else {
sb.append("0");
m = f;
}
if (sb.length() > 34){//二进制最大32位,算上符号位0.一共34
System.out.println("ERROR");
return;
}
}
System.out.println(sb.toString());
}
}