刷刷笔试题--[位运算编程题]

来源:互联网 发布:国密算法实现 编辑:程序博客网 时间:2024/05/17 08:58
1.数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

解析:

最正规的做法:

异或运算的性质:任何一个数字异或它自己都等于0 。

也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,

因为那些出现两次的数字全部在异或中抵消掉了。

如果能够把原数组分为两个子数组。

在每个子数组中,包含一个只出现一次的数字,而其它数字都出现两次。

如果能够这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字了。

我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。

因为其它数字都出现了两次,在异或中全部抵消掉了。

由于这两个数字肯定不一样,那么这个异或结果肯定不为0 ,也就是说在这个结果数字的二进制表示中至少就有一位为1 。

我们在结果数字中找到第一个为1 的位的位置,记为第N 位。

现在我们以第N 位是不是1 为标准把原数组中的数字分成两个子数组,

第一个子数组中每个数字的第N 位都为1 ,而第二个子数组的每个数字的第N 位都为0 。

现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其它数字都出现了两次。

[2*8比2<<3快]

2<<3是左移,是2进制的运算
2*8,还要做很多的运算。
一般,我们都说2进制运算最快。


["<<"这个是左移位运算符],"2<<3"表示2左移3位

2的二进制是00000000 00000000 00000000 00000010
2左移3位,高位的移出,低位的用0填充。
结果:00000000 00000000 00000000 00010000
这个数是16
[m<<n]: 等于m*(2的n次方)

5可以表示为:00000101(最高位表示符号,0位正,1为负)
[>>右移]后为00000010


^  按位异或。[相同为0不同为1]

比如二进制     1001 ^ 1100 = 0101
0^0=0,1^1=0 ,1^0 = 1,0^1=1。

& 按位与
int a = 10;
int b =2;
a&b=2 ,按位与,算术运算..1010&0010 = 0010
a&&b = true 并且,逻辑运算.


/* * 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 */public class xorProblem {public void FindNumsAppearOnce(int[] array,int num1[],int num2[]){if(array==null||array.length==0)return;int temp=0;for(int i=0;i<array.length;i++){temp^=array[i];//异或数组中所有的数字}//index1是指那两个数异或时第一个出现1的位置int index1=findFirstBitOne(temp);//遍历数组,按那一位是1还是0把数组分成两个数组for(int i=0;i<array.length;i++){if(isBit(array[i],index1))num1[0]^=array[i];elsenum2[0]^=array[i];}}//寻找一个数的二进制表示中,第一个1出现的位置,从右到左开始找public int findFirstBitOne(int num){int index=0;//num中的二进制位不停地和1按位与,index标记位数//不要超过int的bit数就好,int四个字节,每个字节8bit,所以8*4,最好直接写成32while(((num&1)==0)&&(index)<8*4){num=num>>1;//对num进行右移,把最右边比较完的数挤出去++index;}return index;}//给出之前找到的index,判断其他数,这一位是不是1public boolean isBit(int num,int index){num=num>>index;return (num&1)==1;}}


2.不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

解析:

一看到这种题就知道要用位运算来解决

let's have a good look

首先看十进制是如何做的:5+7=12

第一步:相加各位的值,不算进位,得到2

第二步:计算进位值,得到10,如果这一步的进位值为0,那么在这一步终止,得到最终结果

第三步:重复上两步,相加的值变成了2+10,得到12,没有进位,进位值为0,循环终止,否则一直相加,直到进位值为0


同样,用这三步来计算二进制相加:5->101,7->111,

第一步:相加各位的值,不算进位,二进制每位相加就相当于做异或操作,101^111=010[相同为0不同为1]

第二步:计算进位值,相当于二进制各位数与,再向左移一位,(101&111)<<1  =  101<<1=1010

第三步:重复上两步:010^1010=1100,(010&1010)<<1=  0,进位值为0,跳出循环,1100为最终结果


public class Solution {    public int Add(int num1,int num2) {        while(num2!=0){            int temp=num1^num2;            num2=(num1&num2)<<1;            num1=temp;        }        return num1;    }}











0 0
原创粉丝点击