43.数组中唯一出现一次的数(278)
-
题目描述:在一个数组中,除一个数字只出现一次,其他的数字都出现三次,找出只出现一次的数字。
-
思路:只有一个数字出现一次,其他的数字都出现三次,那么对数组中的二进制的每位求和,每一位要么是3的倍数,要么比大1,当是3的倍数时,只出现一次的数 的对应位为0,否则为1。10 ,10,10,01(二进制)将对应位求和之后为:3,1;然后再对求和之后的每一位对3求余数:0,1 。则0,1 为只出现一次的数的二进制。
相关数字对应二进制表示:10 = 1010 4 = 0100 5 = 0101 1)申请一个长度为32(因为int占32bit)的int型数组bitsum,用于记录整型数字每一位出现1的次数。 bitsum = 00000000 00000000 00000000 00000000 2)将每个数字计入到bitsum[]中 10-> 00000000 00000000 00000000 00001010 4 -> 00000000 00000000 00000000 00001110 5 -> 00000000 00000000 00000000 00001211 5 -> 00000000 00000000 00000000 00001312 4 -> 00000000 00000000 00000000 00001412 4 -> 00000000 00000000 00000000 00001512 5 -> 00000000 00000000 00000000 00001613 3)bitsum的每一个元素对3取模: bitsum = 00000000 00000000 00000000 00001010 4)将bitsum作为一个32bit的整数: result = 00000000 00000000 00000000 00001010 = 10
-
代码
package _43.数组中唯一出现一次的数; import java.util.Arrays; /** * 题目描述:在一个数组中,除一个数字只出现一次,其他的数字都出现三次,找出只出现一次的数字。 * @author Administrator * */ public class NumberAppearOnce { public static int findNumberAppearOnce(int[] data){ if(data == null || data.length < 3) return -1; //1.计算数组中每一位的和 int[] bit = new int[32]; int flag = 1; for(int i = 0; i < data.length; i++){ flag = 1; for(int j = 31; j >= 0; j--){ if((data[i]&flag) != 0){// !=0:表示该位 为1;不能用==1来判断,1010&1000=1000=8 bit[j] += 1; } flag <<= 1; } } System.out.println(Arrays.toString(bit)); //2.每一位的和3求余数,求余之后的二进制就是只出现一次的数字 int result = 0; for(int i = 0; i < bit.length; i++){ result <<= 1; //乘以2(例如:124 = (1*10+2)*10+4) result += bit[i] % 3; } //3.返回结果 return result; } public static void main(String[] args) { int[] data = {10,4,5,3,5,4,3,3,4,5}; int[] data1 = {0,-4,5,3,5,-4,3,3,-4,5}; int[] data2 = {-10,-4,5,3,5,-4,3,3,-4,5}; int appearOnce = findNumberAppearOnce(data); int appearOnce1 = findNumberAppearOnce(data1); int appearOnce2 = findNumberAppearOnce(data2); System.out.println(appearOnce); System.out.println(appearOnce1); System.out.println(appearOnce2); } }
总结:
(1)所有元素都出现了2次,只有一个只出现了1次。
将所有的数去异或(|)运算,最终结果就为只出现一次的整数。因为亦或有如下两条关键性质:
(1)ab=ba,意味着对一个无序数组进行亦或等价于对排序后的数组进行疑惑
(2)aba=b,意味着出现两次的数字可以抵消掉。
因此,将数组中所有元素进行亦或,即可得到那个只出现一次的一个数字。。
(2)所有的元素都出现了2次,只有两个数只出现了1次。
两个不一样的数值异或,结果的二进制中至少有一个1。将所有的数做异或运算之后(结果就为这两个不一样的数做异或运算),根据 1 出现的位置,将该位置为 0 的分为一组,为 1 的分为一组,这样一定可以将这两个不一样的数分开,而对于相同的数,一定是分在一组的。然后再分别对两组数据进行异或运算,分别得出两组数据中不一样的那个数。
(3)所有的元素都出现了3次,只有一个只出现了1次。
如果一个数字出现3次,那么它的二进制表示的每一位(0或1)也出席3次,如果把所有出现3次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被3整除。例如:01、01、01;三个0,三个1 。
如果只出现一次的那个数某一位为0的话,那么该位的和就能被3整除;如果那一位为1的话,那么该位就不能被3整除,余数为1 。
结论:将元素的每一位求和并保存(32位的数组),然后让每一位和对3求余,最终整型数组中表示的二进制数就为只出现一次的数的二进制。
(4)所有的元素都出现了K次,只有一个只出现了1次。
和(3)一样的思路,将3改为k即可。
(5)所有的元素都出现了K次,只有一个出现了M次(0<m<k)
沿用(3)的思路,将元素的所有的位分别求和之后,对k求余,余数要么为0,要么为m(>0),而第三题是求余之后要么为0,要么为1 。只用改动(3)中求余结果的代码即可。
//2.每一位的和3求余数,求余之后的二进制就是只出现2次的数字(k=3;m=2)
int result = 0;
for(int i = 0; i < bit.length; i++){
result <<= 1; //乘以2(例如:124 = (1*10+2)*10+4)
result += (bit[i] % 3) > 0 ? 1 : 0;
}