位运算
1、位运算符
两个数之间进行位运算是逐位运算!
运算符 | 含义 | 规则 | 例如 |
---|---|---|---|
& | 与 | 任何二进制位和0运算,结果都是0;和1进行,结果都是原值;二者都是一,才为一 | a & b |
| | 或 | 任何二进制位和0运算,结果都是原值;和1进行,结果都是1;二者有一个是一,则为一 | a | b |
~ | 取反 | 0 -> 1 ;1 -> 0 零变成一,一变成零 | ~a |
^ | 异或 | 二者相同为0,不同则为1 相异为真 | a ^ b |
<< | 左移 | 每向左移 1 位,就相当于 *** 2** | a<<b |
>> | 右移 | 每向右移 1 位,就相当于 / 2 用符号位来填充高位 | a>>b |
>>> | 右移 | 用0来填充高位 | a>>>b |
相关性质:
1、交换律:可任意交换运算因子的位置,结果不变
2、结合律:即(a ^ b)^ c = a ^ (b ^ c)
3、对于任何数x,都有x ^ x=0, x ^ 0=x,同自己求异或为0,同 0求异或为自己
4、自反性: A ^ B ^ B = A ^ 0 = A
2、简单入门
第一题:判断奇偶数
& 1 = 1 (奇数) & 1 = 0 (偶数)
参考代码:
Scanner scan = new Scanner(System.in);
int n1 = scan.nextInt();
System.out.println(n1&1);
输出示例:
& 1 = 1 (奇数)
17
1
& 1 = 0 (偶数)
6
0
第二题:获取二进制位是1还是0
( 1 << (x-1) ) & num) >> (x-1) x:求第x位二进制位数上是0还是1 num:原数
意在先左移来做与运算,看这一位二进制位数是0还是1,最后右移,将结果与1进行比较,如相同则返回1,不同则为0
参考代码:
Scanner scan = new Scanner(System.in);
int n1 = scan.nextInt();
System.out.println("n1的第五位二进制位的数:" + (((((1<<4)&n1)>>4)==1)?"1":"0"));
输出示例:
66的二进制为: 01011000
左移四位相当于: 01011000 & 10000 ==> 1&1=1 (&:二者都是一,才为一)
88
n1的第五位二进制位的数:1
66的二进制为: 01000010
左移四位相当于:1100100 & 10000 ==> 0&1=0 (&:二者都是一,才为一)
66
n1的第五位二进制位的数:0
第三题:交换两个整数变量的值
使用三条异或语句
n1 = n1 ^ n2;
n2 = n1 ^ n2;
n1 = n1 ^ n2;
如:
int n1 = 10、int n2 = 15;
10的二进制为:1010 、20的二进制为:1111
对于异或来说,二者相同为0,不同则为1 n1 ^ n2 = 0 !
第一次n1 ^ n2后,1010 与 1111 相比,结果为:0101 ,此时二进制为0101,赋给了n1
第二次n1 ^ n2后,0101 与 1111 相比,结果为:1010,此时二进制为1010,赋给了n2
第三次n1 ^ n2后,0101 与 1010 相比,结果为:1111,此时二进制为1111,赋给了n1
此时就完成了n1和n2的交换辣!
参考代码:
int n1 = 10;
int n2 = 15;
n1 = n1 ^ n2;
n2 = n1 ^ n2;
n1 = n1 ^ n2;
System.out.println("n1="+n1);
System.out.println("n2="+n2);
输出示例:
n1=15
n2=10
第四题:不用判断语句,求整数的绝对值
x + (x >> 31)) ^ (x >> 31)
与前面的题目如出一辙,这边就省略了嗷qwq
3、综合练习
第一题:找出唯一成对的数
1-1000这1000个数放在含有1001个元素的数组中, 只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次, 设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?
int n = 11;
int[] arr = new int[n];
for(int i = 0;i<arr.length-1;i++)
{
arr[i] = i+1;
}
arr[arr.length-1]=new Random().nextInt(n-1)+1;//设置最后一个数为随机数
int index = new Random().nextInt(n);//随机下标
swap(arr,index,arr.length-1);//交换
print(arr);
int x = 0;
for(int i = 1; i <= n-1; i++)
{
x = (x^i);//得1到n-1连续的异或
}
for(int i = 0; i < n;i++)
{
x = x^arr[i];
}
System.out.println("重复数字:" + x );
注:本篇代码还运用了两个方法:
swap方法:将数组里的数,重复的数随机放置
public static void swap(int[] arr, int p,int q)
{
int temp = arr[p];
arr[p] = arr[q];
arr[q] = temp;
}
print方法:把这个数组里的每一个数都打印出来
public static void print(int[] arr)
{
for(int i = 0; i < arr.length; i++)
{
System.out.print(arr[i]+" ");
}
System.out.println();
}
且这道题其实用辅助存储空间解起来更方便点,所以我在下面也放进去了,就不多扩展了嗷(●’◡’●)ノ
参考代码:
import java.util.Random;
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Dog
{
public static void main(String[] args)
{
int n = 11;
int[] arr = new int[n];
for(int i = 0;i<arr.length-1;i++)
{
arr[i] = i+1;
}
arr[arr.length-1]=new Random().nextInt(n-1)+1;//设置最后一个数为随机数
int index = new Random().nextInt(n);//随机下标
swap(arr,index,arr.length-1);//交换
print(arr);
int x = 0;
for(int i = 1; i <= n-1; i++)
{
x = (x^i);//得1到n-1连续的异或
}
for(int i = 0; i < n;i++)
{
x = x^arr[i];
}
System.out.println("重复数字:" + x );
System.out.println("尝试利用辅助存储空间来解本题");
int[] helper = new int[n];
for(int i = 0;i<n;i++)
{
helper[arr[i]]++;
}
for(int i = 0;i<n;i++)
{
if(helper[i]==2)
{
System.out.println("重复数字:"+i);
}
}
}
public static void print(int arr[])
{
for(int i = 0; i < arr.length; i++)
{
System.out.print(arr[i]+" ");
}
System.out.println();
}
public static void swap(int arr[], int p,int q)
{
int temp = arr[p];
arr[p] = arr[q];
arr[q] = temp;
}
}
输出示例:
1 2 6 4 5 6 7 8 9 10 3
重复数字:6
尝试利用辅助存储空间来解本题
重复数字:6