NO1. 给定两个32位的整数N与M,以及表示比特位置的i与j。编写一个方法,将M插入N,使得M从N的第j位开始,到第i位结束。假定从j位到i位足以容纳M,也即若M=10011,那么j和i之间至少可容纳5个位。例如,不可能出现j=3和i=2的情况,因为第3位和第2位之间放不下M。
示例:输入:N=1000 0000 0000 0000, M=1 0011,i=2,j=6
输出:N=1000 0000 0100 1100.
解法:1、清除N的j到i位,可使用掩码11111000111与N相与即可。
2、将M左移i-1位
3、将M和N进行或操作即可。
int updateBits(int n,int m,int j,int i){
int temp=((~0)<<j)|((1<<i)-1);//表示111110000011111
n&=temp;//清空j到i之间的所有数,置为零
m<<=(i-1);
return m|n;
}
NO2. 给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表示。如果该数字无法精确地用32位以内的二进制表示,则打印“ERROR”。
解法:对于十进制整数化为二进制采用除2取余的方法,对于十进制整数则采用乘2取整的方法。
void printBinary(double num){
if(num>=1||num<=0)
return "ERROR";
string str("0.");
while(num){
if(str.size()>=32)
return "ERROR";
num<<=1;
if(num>=1){
num-=1;
str+='1';
}
else{
str+='0';
}
}
cout<<str<<endl;
}
NO3. 给定一个正整数,找出与其二进制表示中1的个数相同、且大小最接近的那两个数(一个略小,一个略大)。
解法1:蛮力法,找出目标数字中1的个数,分别从该数字开始向两端扩散,直到某个数字跟目标数字中1的个数一样。
解法2:位操作法,首先找较大的数,记数字的尾部0的个数为c0,记数字最右端连着的1的个数为c1,令p=c0+c1+1,则p位为0,将p位改为1,在1--p-1位先清零,然后在最右端添加c1-1个1即可。然后找较小的数,记数字尾部1的个数为c1,记数字最右端连着的0的个数为c0,令p=c0+c1+1,则p位为1,将p为置为0,在1--p-1位先清0,然后在最左端添加c1+1个1即可。
解法3:算术解法,在位操作的基础上,根据c0和c1关系用数学表达式直接返回,代码更加简洁。
NO4. 解释代码((n&(n-1))==0)的具体含义。
a&b==0说明,a和b的取反,所有的二进制位都不相同,那么n和n-1具有取反关系,则表明n为2的指数幂。
NO5. 编写一个函数,确定需要改变几个位,才能将整数A转成整数B。示例,输入:31,14。输出:2
解法:A异或B得到了A和B中所有二进制位的关系,若相同就为0,若不同就为1,所以只需统计A^B中1的个数,在统计1的个数时,可以采用移位和1相与的操作完成,还可以采用一个更好的方法N&(N-1),检查最低有效位。
NO6. 编写程序,交换某个整数的奇数位和偶数位,使用指令越少越好(也就是说,位0与位1交换,位2与3交换,依次类推)。
解法:取0xAAAA为掩码和整数与操作,得到奇数位数字,向右移位1次,取0x5555为掩码和整数与操作,得到偶数位数字,向左移位1次,二者相或得到结果。
NO7. 数组A包含0到n的所有整数,但其中缺了一个。在这个问题中,只用一次操作无法取得数组A里某个整数的完整内容。此外,数组A的元素皆以二进制表示,唯一可用的访问操作是“从A【i】取出第j位数据”,该操作的时间复杂度为常数。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
解法:若是可以直接访问所有整数,那我们可以计算所有整数的和,然后用公式n*(n+1)/2减去它就得到了缺少的数,此题也可通过计算每一个数的十进制然后利用以上方法,不过时间复杂度较高O(N*logN)。这里只能根据每一位中0和1的总个数的比较判断缺少数值在该位为0还是1了,若n为奇数,则总共有偶数个数,每一位的0和1应该相等,若n为偶数,则总数为奇数,每一位的0的个数和1中多一个。
NO8. 有个单色屏幕存储一个一维字节数组中,使得8个连续像素可以存放在一个字节里。屏幕宽度为w,且w可被8整除(即一个字节不会分布在两行上),屏幕高度可由数组长度及屏幕宽度推算得出。请实现一个函数drawHorizontalLine(byte[] screen,int width,int x1,int x2,int y),绘制从点(x1,y)到点(x2,y)的水平线。
解法:粗糙解法,for循环迭代逐个点打印,巧妙解法,逐个字节操作,两端分别操作。