题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
题目提示:num1,num2分别为长度为1的数组。传出参数;将num1[0],num2[0]设置为返回结果
3.第三次做 题目变形 依然位运算
题目二:数组中唯一只出现一次的数字
在一个数组中除一个数组只出现一次之外,其他数字都出现了三次,
请找出那个只出现一次的数字。
思路:之前的思路不能解决这个问题,因为三个相同的数组异或结果还是该数字。还是沿用位运算的思路:如果一个数字出现三次,它的二进制表示的每一位(0或1)也出现三次。每一位的和都能被3整除。我们把数组中所有数字的二进制表示的每一位都加起来。如果某一位的和能被3整除,那么那个只出现一次的数字二进制表示中对应的那一位是0,否则就是1。
public static int FindNumberAppreaingOnce(int [] array) { if(array == null) return 0; int[] bitMask = new int[32]; int i, j, res = 0; for(i = 0; i<array.length; i++){ for(j = 31; j>=0; j--){ bitMask[j] += array[i]&1; array[i] = array[i]>>1; } } for(j = 0; j<32; j++){ res = (res<<1) + bitMask[j]%3; } System.out.println(res); return res; }
2.第二次做:位运算 异或思路
看了讨论和题解,果然不是哈希这么简单。思路的核心是:两个相同数字异或=0,一个数和0异或还是它本身。沿着这个思路,关键问题是如何将数组分成两个部分,每一部分只包含一个解。这样每部分异或下来得到的就是一个解。讲数组分成两部分的方案:
根据异或的结果1所在的最低位:
我们首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。
//num1,num2分别为长度为1的数组。传出参数 //将num1[0],num2[0]设置为返回结果 public class Solution { public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { int res = 0, count = 0, i; for(i = 0; i<array.length; i++){ res ^= array[i]; } //取第一个为1的位数作为分组标准 while((res&1)==0){ res = res>>1; count++; } for(i = 0; i<array.length; i++){ if((array[i]&(1<<count))==0){ num1[0] = num1[0]^array[i]; } else{ num2[0] = num2[0]^array[i]; } } } }
知识库中丰富的Tips:
1. 取数的某一特定二进制位的值——(右移特定位&1)
2. 判断某数特定二进制位的值————(1左移特定位&该数 判断是否为0)
1.第一次做:hash用空间换时间
public class Solution { public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { int[] hasharray = new int[10000000]; for(int i=0; i<array.length; i++){ hasharray[array[i]]++; } int count=0; for(int i=0; i<hasharray.length; i++){ if(hasharray[i]==1 && count==0){ num1[0]=i; count++; } if(hasharray[i]==1 && count==1) num2[0]=i; } } }