程序员面试——位运算
5.1 二进制插入
有两个32位整数n和m,请编写算法将m的二进制数位插入到n的二进制的第j到第i位,其中二进制的位数从低位数到高位且以0开始。
给定两个数int n和int m,同时给定int j和int i,意义如题所述,请返回操作后的数,保证n的第j到第i位均为零,且m的二进制位数小于等于i-j+1。
1024,19,2,6
返回:1100
画图——》移位+或
import java.util.*; public class BinInsert { public int binInsert(int n, int m, int j, int i) { return n|(m<<j); } }
5.2 二进制小数
有一个介于0和1之间的实数,类型为double,返回它的二进制表示。如果该数字无法精确地用32位以内的二进制表示,返回“Error”。
给定一个double num,表示0到1的实数,请返回一个string,代表该数的二进制表示或者“Error”。
0.625
返回:0.101
照着二进制和十进制小数的数学关系做就是了:乘2与1比
import java.util.*; public class BinDecimal { public String printBin(double num) { StringBuffer res=new StringBuffer(); res.append("0"); res.append("."); for(int i=0;i<33;i++) { if(i==32) return "Error"; num=num*2; if(num-1==0){ res.append("1"); break; }else if(num-1>0){ res.append("1"); num=num-1; }else{ res.append("0"); } } return res.toString(); } }
当然,有时间可以试试跟0.5之类的做减法;思路差不多
5.3 最接近的数
蛮力法都还没做出来,等脑子清醒一点再做;
5.4 ((n&(n-1))==0)的含义
从特殊到一般再到特殊:A&B==0
5.5 整数转化
编写一个函数,确定需要改变几个位,才能将整数A转变成整数B。
给定两个整数int A,int B。请返回需要改变的数位个数。
10,5
返回:4
计数就好,注意用while
import java.util.*; public class Transform { public int calcCost(int A, int B) { // 直接的思路,直接干,然后计数就好了嘛 int count=0; int index=0; if (A==B) return count; while (A!=B) { if((A&(1<<index))!=(B&(1<<index))){ count++; A=A^(1<<index); } index++; } return count; } }
细节:用num^(1<<index)来改变某一位的值;
5.6 奇偶位交换
请编写程序交换一个数的二进制的奇数位和偶数位。(使用越少的指令越好)
给定一个int x,请返回交换后的数int。
10
返回:5
import java.util.*; public class Exchange { public int exchangeOddEven(int x) { // 首先,实现功能,就是从头开始,两位两位的跳 //问题在于如何判断结束,不如再弄一个数字,慢慢赋值 int[] tmp=new int[2]; //int y=0; //int res=0; int index=0; // int slow=0; tmp[0]=x;tmp[1]=0; while(x!=tmp[1]){ swap(tmp,index); index=index+2; } return tmp[0]; } void swap(int[] tmp,int index) { int first=0; int second=0; int xx=tmp[0]; //int yy=tmp[1]; first=xx&(1<<index); second=xx&(1<<(index+1)); if (first!=0) first=1; if (second!=0) second=1; //先清零再置位 tmp[0]=(tmp[0]&(~(1<<index)))|(second<<index); tmp[0]=(tmp[0]&(~(1<<(index+1))))|(first<<(index+1)); //更新y tmp[1]=(tmp[1]|(first<<index))|(second<<(index+1)); } }
关键点在于while的停止条件吧,对于我而言;
巧妙方法:先对所有奇数位操作,再是偶数位,再或一下
return (((x & 0xaaaaaaaa)>>1)) | (((x & 0x55555555)<<1));
5.7 找出缺失的整数
数组A包含了0到n的所有整数,但其中缺失了一个。对于这个问题,我们设定限制,使得一次操作无法取得数组number里某个整数的完整内容。唯一的可用操作是询问数组中第i个元素的二进制的第j位(最低位为第0位),该操作的时间复杂度为常数,请设计算法,在O(n)的时间内找到这个数。
给定一个数组number,即所有剩下的数按从小到大排列的二进制各位的值,如A[0][1]表示剩下的第二个数二进制从低到高的第二位。同时给定一个int n,意义如题。请返回缺失的数。
[[0],[0,1]]
返回:1
没意思。。不做。。
按照书上的思路:一般的是全部加起来,少那个就是那个;
这个的话,就奇偶缺失来做就行!!!
总结
5.3 和5.8没搞定,5.7不想做,其他做完了,对位运算,还是熟练是第一位的,思路要灵活些,什么快慢指针,递归,数组,字符串都可以用起;
---------------拓展--------------------------
1.1&1.7 见 数组与字符串http://www.cnblogs.com/andy1202go/p/5759047.html
17.1 无缓存交换
请编写一个函数,函数内不使用任何临时变量,直接交换两个数的值。
给定一个int数组AB,其第零个元素和第一个元素为待交换的值,请返回交换后的数组。
[1,2]
返回:[2,1]
我的思路是直接的公式退出来就好啊:
import java.util.*; public class Exchange { public int[] exchangeAB(int[] AB) { AB[0]=AB[0]+AB[1]; AB[1]=-(AB[1]-AB[0]); AB[0]=AB[0]-AB[1]; return AB; } }
书上的位运算解法没看懂。。。。
a=a^b; b=a^b;//懂了,b=(a^b)^b=a; a=a^b;
妈蛋,有点厉害!!!
18.1 另类加法
不用加号的加法
主体思路:位运算;
一般:捣腾出进位再进行相加之类的
public class Solution { public int getSum(int a, int b) { int[] array=new int[]{a,b}; getReal(array); int sum=array[0]|array[1]; return sum; } public void getReal(int[] array) { int jinWei=array[0]&array[1]; int mask=~jinWei; int huo=array[0]|array[1]; array[0]=mask&huo; array[1]=jinWei<<1; int state=array[0]&array[1]; if (state!=0){ getReal(array); } } }
进阶:高级一点的递归
import java.util.*; public class UnusualAdd { public int addAB(int A, int B) { if (A==0) return B; if (B==0) return A; int a=A^B,b=(A&B)<<1; return addAB(a,b); } }
18.4 有几个2
请编写一个方法,输出0到n(包括n)中数字2出现了几次。
给定一个正整数n,请返回0到n的数字中2出现了几次。
10
返回:1
一般思路,每一个数这么看下来就好
import java.util.*; public class Count2 { public int countNumberOf2s(int n) { //要全部,看起来就像是递归 int count=0; if (n<2) return count; for(int i=2;i<=n;i++) { count+=num2(i); } return count; } int num2(int n){ int count=0; while(n>0){ if (n%10==2){ count++; } n=n/10; } return count; } }
算法复杂度太大,时间太长。
找规律,得下面(我tm还没搞懂)
import java.util.*; public class Count2 { public int countNumberOf2s(int n) { int result = 0; for(int i=1;i<=n;i*=10) { result+=(n/i+7)/10*i+(n/i%10==2?n%i+1:0) ; } return result; } }
每十倍循环一下,然后对这个十倍中有多少2进行运算。
最开始想的是用递归,不是很好做,而且也是每一个这么搞,时间也太长。