算法描述
在左程云左神的算法课上,有这样一道例题:
已知数组int[] arr :
Q1:在arr中,只有一种数出现了奇数次,其余数均出现偶数次,请找出这个出现了奇数次的数。
Q2:在arr中,有两种数出现了奇数次,其余数均出现偶数次,请找出这两个出现了奇数次的数。
例:int[] {2,2,3,3,4,4,5,5,5},5出现了3次,奇数次。其余数字均出现两次,偶数次。
异或运算的性质
异或运算:按位运算,如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。符号xor,记作^。
例:a=00101011b,b=10110100b,a^b=10010001b
性质:
1.归零率:a^a=0;
2.恒等率:a^0=a;
3.交换律:a^b=b^a;
4.结合律:a^b^c=(a^b)^c=a^(b^c);
5.自反:a^b^a=b;
Q1:一种数出现了奇数次
利用异或运算的性质1归零率,偶数次出现的数依次疑惑,结果为0。奇数次出现的数依次异或,结果为数本身。再根据性质2恒等率,一个数异或0则等于其本身。 这样将数组里所有的数依次异或,得到的结果则是出现了基数次的那个数。
public static void PointOddTimesNumQ1Func(int[] arr){
int eor = 0;
for (int cur : arr) {
eor^=cur;
}
System.out.println(“出现基数次的数为:”+eor);
}
Q2:两种书出现了奇数次
假设a出现了奇数次,b出现了奇数次,首先表明a≠b,所以a^b不等于0。由此可得,a与b的二进制数,必然存在某一位不相同。
第一步:我们首先,将数组内的所有元素依次异或,根据异或运算的性质,得int[] arr依次异或的结果=a^b;我们记作eor=a^b;
第二步:找到a与b的二进制数不相同的最低位
例:a = 10001000b,b=0110000b,a与b的第三位不同,则这一位的异或结果为1,我们去尝试找到这一位。
eor=a^b=11101000,则最低位=eor&(~eor+1)=00001000b,我们记作rightBit,我们可得到,a与b的二进制的第3位肯定是不相同的。
第三步:遍历整个数组,每一个数字与rightBit进行与运算(cur&rightBit),如果得到的结果为0,则表示,有可能这个数为a/b其中的一个,但是如果是a,则表示a的二进制在第3位肯定为0,则b的第3位肯定为1。这样我们就能将a和b划分清楚界限。或者用cur&rightBit==rightBit判断,这样筛选出来的所有数,都是第3位为1的数,因为a和b是相对独立的,则非a即b。
第四步:将第三步筛选出来的所有数进行依次异或运算。因为其他数都是偶数次出现,所以不管筛选出来其他数第三位是什么,他们自身异或的结果都是0。这样就相当于,对a或者b做了一次独立异或。异或的结果则是a或b,这样就找出了a/b。eor=a^b,我们用a/b其中的一个,去异或eor,则得到另外一个数。
注意:第三四步的核心思想,其实是怎么将a和b这两个出现了奇数次的数字进行分割。
public static void PointOddTimesNumQ2Func(int[] arr) {
int eor = 0;
for (int cur : arr) {
eor^=cur;
}
int rightBit =0;
rightBit = eor & (~eor+1);
int eorAnother=0;
for (int cur : arr) {
if ((cur&rightBit)==rightBit)
eorAnother^=cur;
}
System.out.println(eorAnother +"and"+ (eorAnother^eor));
}
测试数组:new int[]{1,1,2,2,50,50,50,60,60,60}
以上为(cur&rightBit)==rightBit去分割a和b,测试结果为:
按照(cur&rightBit)==0条件去分割a和b,测试结果为:
这两个结果只是,计算出来的先后顺序不同,区别在乎你用什么样的条件,先去找出那个不同位为0的还是为1的。
补充:找两个数的不相同的最低位
例:a^b≠0,找出a和b二进制数的不相同的最低位。
方法为:eor=a^b,eor&(~eor+1)
可以看到,a和b的二进制,在第4位开始不同。
这里本人斗胆提一句:当时在听左老师的课程时,左老师讲的是(cur&rightBit)==0和(cur&rightBit)==1是两个对立的条件,本人当时没听懂,经过Debug发现,这里左老师说错了,0应该和rightBit是对立条件,分别找出来,不同位次为1或0的数字。如果大家对这一点有疑问的话,建议自己Debug,会解答你自己的疑惑,或者评论区一起讨论。
算法真的太美了,希望大家都能坚持下去!!!!!!