位运算练习
1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次。每个数组元素只能访问一次,设计一个算法,将他找出来;不用辅助存储空间,能否设计一个算法实现?
解析:一个数和本身异或为0,任何数和0异或还是这个数。比如2和2异或为0,那么2和2和2异或就为2。
1 import java.util.Random; 2 public class 唯一成对的数 { 3 public static void main(String[] args) { 4 int N=1001; 5 int[] arr=new int[N]; 6 int len=arr.length; 7 for(int i=0;i<len-1;i++) 8 { 9 arr[i]=i+1; 10 } 11 12 //最后一个数,是随机数 13 arr[len-1]=new Random().nextInt(N-1)+1; 14 //随机下标 15 int index=new Random().nextInt(N); 16 17 //交换随机下标的数和随机数 18 int temp=arr[index]; 19 arr[index]=arr[len-1]; 20 arr[len-1]=temp; 21 22 for(int i=0;i<N;i++) 23 System.out.print(arr[i]+" "); 24 25 System.out.println(); 26 27 int x1=0; 28 //先将x1与1~N-1异或 29 for(int i=1;i<=N-1;i++) 30 x1=(x1^i); 31 //再将x1与数组中各个数异或 32 for(int i=0;i<N;i++) 33 x1=(x1^arr[i]); 34 35 System.out.println(x1); 36 } 37 }
找出落单的那个数
一个数组里除了某一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。
解析:只需全部异或,异或的结果就是答案。因为成对的数都相互抵消了。
二进制中1的个数
请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。
例:9的二进制表示为1001,有2位是1。
法一:将数字1每次左移一位(就是1<<i),并与N进行与运算,如果结果等于(1<<i),那么个数加1
1 import java.util.Scanner; 2 public class _1的个数 { 3 public static void main(String[] args) { 4 Scanner sc=new Scanner(System.in); 5 int N=sc.nextInt(); 6 System.out.println(Integer.toString(N,2)); 7 int count=0; 8 for(int i=0;i<32;i++) 9 { 10 if((N&(1<<i))==(1<<i)) 11 { 12 count++; 13 } 14 } 15 16 System.out.println(count); 17 } 18 }
法二:将N每次右移一位,并与1与运算,如果结果为1,则个数加1
法三:整数N,N=(N-1)&N就是将N的最右边的1消除。
1 0 1 0 0
-1 1 0 0 1 1
&——————
1 0 0 0 0
-1 0 1 1 1 1
&——————
0 0 0 0 0
1 import java.util.Scanner; 2 public class _1的个数 { 3 public static void main(String[] args) { 4 Scanner sc=new Scanner(System.in); 5 int N=sc.nextInt(); 6 System.out.println(Integer.toString(N,2)); 7 int count=0; 8 9 while(N!=0) 10 { 11 N=(N-1)&N; 12 count++; 13 } 14 15 System.out.println(count); 16 } 17 }
是不是2的整数次方
用一条语句判断一个整数是不是2的整数次方。
解析:上题的思路 if(N&(N-1)==0)
将整数的奇偶位互换
将一个整数二进制表示的奇偶位互换
例:9的二进制:1001,互换后为0110
解析:将这个数与...10101010进行与运算,将偶数位提取,再与...01010101进行与远算,将奇数位提取。最后将提取到的偶数位右移一位,奇数位左移一位,两者异或,即得到互换效果。
1 public class 奇偶位互换 { 2 public static void main(String[] args) { 3 int a=9; 4 int b=m(a); 5 System.out.println(Integer.toString(a,2)); 6 System.out.println(Integer.toString(b,2)); 7 } 8 9 public static int m(int i) 10 { 11 int ou=i&0xaaaaaaaa;//和1010 1010 1010 。。。做与运算取出偶数位 12 int ji=i&0x55555555;//和0101 0101 0101 。。。做与运算取出奇数位 13 return (ou>>1)^(ji<<1); 14 } 15 }
0~1间浮点实数的二进制表示
给定一个介于0和1之间的实数(如0.625),类型为double,打印它的二进制表示(0.101),如果该数字无法精确地用32位以内的二进制表示,则打印“ERROR”
1 public class 二进制小数 { 2 public static void main(String[] args) { 3 double num=0.875; 4 StringBuilder sb=new StringBuilder("0."); 5 while(num>0) 6 { 7 double r=num*2; 8 if(r>=1) 9 { 10 sb.append("1"); 11 num=r-1; 12 } 13 else 14 { 15 sb.append("0"); 16 num=r; 17 } 18 if(sb.length()>34) 19 { 20 System.out.println("ERROR"); 21 return; 22 } 23 } 24 System.out.println(sb.toString()); 25 } 26 }
出现k次与出现1次
数组中只有一个数出现了1次,其他的数都出现了k次,请输出出现了1次的数。
解析:
2个相同的2进制数做不进位加法,结果为0
10个相同的10进制数做不进位加法,结果为0
k个相同的k进制数做不进位加法,结果为0
1 public class 出现k次 { 2 public static void main(String[] args) { 3 int[] arr= {2,2,2,9,7,7,7,3,3,3,6,6,6,0,0,0}; 4 int len=arr.length; 5 char[][] kRadix=new char[len][];//k进制字符数组 6 int k=3; 7 //转成k进制字符数组 8 int maxLen=0; 9 for(int i=0;i<len;i++) 10 { 11 kRadix[i]=new StringBuilder(Integer.toString(arr[i],k)) 12 .reverse().toString().toCharArray(); 13 //记录最长的数组长度 14 if(kRadix[i].length>maxLen) 15 maxLen=kRadix[i].length; 16 17 } 18 19 int[] resArr=new int[maxLen];//结果 20 for(int i=0;i<len;i++) 21 { 22 //不进位加法 23 for(int j=0;j<maxLen;j++) 24 { 25 if(j>=kRadix[i].length) 26 resArr[j]+=0; 27 else 28 resArr[j]+=(kRadix[i][j]-'0'); 29 } 30 } 31 32 //k进制转10进制 33 int res=0; 34 for(int i=0;i<maxLen;i++) 35 { 36 //每一位模k,再乘这一位的权重得到10进制 37 res+=(resArr[i]%k)*(int)(Math.pow(k,i)); 38 } 39 System.out.println(res); 40 } 41 }
参考:《算法很美》