1. 位运算基础
1.1.位运算符
&, |, ^, ~ 四个基本运算符
A&B:1001&1010 = 1000
异或: 如果a、b两个值不相同,则异或结果为1。. 如果a、b两个值相同,异或结果为0
异或运算(^)是以二进制数据为基础进行运算的。也就是说当代码中使用到异或运算时,都会先将两个条件进行转换,转换成二进制数据后,再进行运算。异域中同位如果值相同(都是0或者都是1)则为0,不同(一个是0,一个是1)为1。
将二进制右移(用符号位填充高位) >>
将二进制左移 <<
将二进制右移(用0填充高位) >>>
注意:无<<<
**例:1<<35 = 1<<3 **
System.out.println(1<<3);
System.out.println(1<<35);
输出8(1000)
1.1.1题一:找出唯一成对的数
题目:1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次。每个数组元素只能访问一次,设计一个算法,将他找出来;不用辅助存储空间,能否设计一个算法实现。
方法一:
思路:根据A ^ A ^ B ^ C ^ C = B,所以让数组中的元素全部进行异或运算,再与1-1000进行异或运算,其中出现两次重复的数据异或为0,出现三次的数据异或为它本身,然后打印出来,得到最终结果。
import java.util.Arrays;
import java.util.Random;
public class 如何找数组中唯一成对的那个数 {
public static void main(String[] args) {
int N = 1000;
int[] arr = new int[N];
//1-10放到数组中
for (int i = 0; i < arr.length-1; i++) {
arr[i]=i+1;
}
//随机产生一个随机数放入数组的最后位置
Random random = new Random();
arr[arr.length-1] = random.nextInt(N-1)+1;
//打印数组内容
System.out.println(Arrays.toString(arr));
int x = 0 ;//0与任何数异或还是它本身
//1 - N-1进行异或
for (int i = 0; i < N-1; i++) {
x = x^(i+1);
}
for (int i = 0; i < N; i++) {
x=x^arr[i];
}
System.out.println(x);
}
}
方法二:
思路:用辅助空间,直接暴力破解。创建一个计数器数组helper,将数组arr中出现的数作为索引进行累加,出现两次的那个数在数组helper中对应的内容必为2,然后输出即可。
/暴力破解法============================
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);
break;
}
}
1.1.2题二:找出落单的那个数
题目:一个数组中里除了某一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。
思路:直接根据题一把数组中的元素进行异或即可。具体代码见1.1.1
1.1.3题三:二进制中1的个数
题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。
例:9的二进制表示为1001,二进制位数为2。
方法一:
思路:9=000…0001001, 将1进行左移位,移位32次, 就是进行32次循环,然后1<<i与9进行与运算,得到一个32位的数字,里面肯定只最多只有一个1, 然后让这个数字与1<<i判断是否相等, 相等则计数器加1.
import java.util.Scanner;
public class 二进制中1的个数 {
public static void main(String[] args) {
Scanner scanner =new Scanner(System.in);
int N = scanner.nextInt();
//打印二进制数据
System.out.println(Integer.toString(N,2));
int count = 0;
//比对每一位
for (int i = 0; i < 32; i++) {
if((N&(1<<i)) == (1<<i)) {
count++;
}
}
System.out.println(count);
}
}
输出:
9
1001
2
方法二:
思路:将原数字不断往由移动,每次移动一个数字,步骤和方法一类似.
import java.util.Scanner;
public class 二进制中1的个数 {
public static void main(String[] args) {
Scanner scanner =new Scanner(System.in);
int N = scanner.nextInt();
//打印二进制数据
System.out.println(Integer.toString(N,2));
int count = 0;
//比对每一位
for (int i = 0; i < 32; i++) {
if(((N>>i)&1) == 1) {
count++;
}
}
System.out.println(count);
}
}
方法三:
思路:原数字N每次减一,与N做与运算。1001-1=1000,1000&1001=1000,1000-1=0111,0111&1000=0000
做到第几次为0000,则运算结束。
import java.util.Scanner;
public class 二进制中1的个数 {
public static void main(String[] args) {
Scanner scanner =new Scanner(System.in);
int N = scanner.nextInt();
//打印二进制数据
System.out.println(Integer.toString(N,2));
int count = 0;
//比对每一位
while(N!=0) {
N=(N-1)&N;
count++;
}
System.out.println(count);
}
}
1.1.4题四:判断是不是2的整数次方
题目:用一条语句判断一个整数是不是2的整数次方。
思路:转化成二进制只有一个1
public class 判断是不是二的整数次方 {
public static void main(String[] args) {
int N = 9;
if((N-1)&N) == 0) {
System.out.println(N+"是2的整数次方");
}else{
System.out.println(N+"不是2的整数次方");
}
}
}
1.1.5题五:将整数的奇偶位交换
题目:将整数的奇偶位交换
思路:以9为例,9与…01010进行&运算,将9的偶数位与…01010的偶数位相同的置为1,其余置为0;(1)
9在与…010101进行&预算,将9的奇数位与…01010的奇数位相同的置为1,其余置为0;(2)
然后将(1)右移一位,将(2)左移一位,最后再进行异或运算得到最终结果。
public class 将整数的奇偶位交换 {
public static void main(String[] args) {
int N = 9;
System.out.println(Integer.toString(N,2));
int newN = change(N);
System.out.println(Integer.toString(newN,2));
}
public static int change(int i) {
int ou = i&0xaaaaaaaa ;
int ji = i&0x55555555;
return (ou>>1)^(ji<<1);
}
}
输出:1001
110
1.1.6 0~1间浮点实数的二进制表示
题目:给它定一个介于0和1之间的实数,如0.625,类型位double,打印他的二进制表示0.101;如果该数字无法精确的用32位以内的二进制表示,则打印 ERROR。
思路:将实数N×2,如果大于等于1,将1保留,如果小于1,将0保留。并判断字符串长度是否大于32位,最后输出最终结果。
public class 浮点数的二进制表示 {
public static void main(String[] args) {
StringBuilder sBuilder = new StringBuilder("0.");
double N = 0.625;
while(N>0) {
double NN = N*2;
if(NN >= 1) {
sBuilder.append(1);
N = NN-1;
}
else {
sBuilder.append(0);
N=NN;
}
}
if(sBuilder.length() > 34) {
System.out.println("Error");
}
System.out.println(sBuilder.toString());
}
}
1.1.7 出现K次和出现一次
题目:数组中只有一个数出现了一次,其他的数都出现了K次,请输出只出现了一次的数。
思路:例如:2228777333,可以计数,但是可以用另一种方法:K个相同的K进制数做不进位加法,结果为0。然后可以把只出现了一次的数进行输出。这次感觉暴力更简单
public class 出现一次和出现K次 {
public static void main(String[] args) {
int[] arr = {2,2,2,7,7,7,8,9,9,9};
int[] count=new int[arr.length];
//直接暴力
for (int i = 0; i < arr.length; i++) {
//System.out.println(Integer.toString(arr[i],3));
for (int j = 0; j < arr.length; j++) {
if(arr[i] == arr[j]) {
count[i]++;
}
}
}
//找出计数数组中为1的数
for (int i = 0; i < count.length; i++) {
if(count[i] == 1) {
System.out.println(arr[i]);
}
}
}
}
输出:8