40.数组中只出现一次的数字
题目描述
思路一:
对数组排序,然后遍历数组,判断前后不相等的元素,第一个是num1[0], 第二个数num2[0], 注意如果前后相等的两个元素要跳过一位
1 //num1,num2分别为长度为1的数组。传出参数 2 //将num1[0],num2[0]设置为返回结果 3 import java.util.Arrays; 4 public class Solution { 5 public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { 6 // 排序 7 Arrays.sort(array); 8 9 // 然后遍历数组,判断前后不相等的元素 10 boolean flag = true; // 判断是第一个满足要求的数字 11 for(int i = 0; i < array.length; i++){ 12 if(i + 1 < array.length && array[i] == array[i + 1]){ 13 i++; // 跳过下一个数 14 }else { 15 if(flag){ 16 num1[0] = array[i]; 17 flag = false; 18 }else{ 19 num2[0] = array[i]; 20 } 21 } 22 } 23 } 24 }
思路二:
利用异或运算
异或运算的规则是:两个相同数字异或=0,一个数和0异或还是它本身。
当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。所以我们尝试把原数组分成两个数组,每个数组分别含有一个只出现一次的数字,且每个数组中其他元素均是成对出现的。
分成这样两个数组的过程是:假设 两个只出现一次的数字分别为 A 和 B
1. 定义一个变量 xor ,遍历数组,让这个变量与每个元素都进行一次异或运算,最终的结果肯定是 A 与 B 做异或的结果,因为其他元素都成对出现抵消为0 了。
2. 从低位开始,找到 xor 的二进制表示中第一个1 出现的位置, 假设位置为 p ,按照异或的规则,这个位置的值既然 是 1,那么说明 A 和 B两个元素在该位置的值肯定不同,肯定是一个是0,一个是1
3. 定义一个变量 index ,初值为 1, 让它左移 p 位,这样 index 的二进制表示中只有 p 位 为 1, 其他位均为0, 让 index 分别与元素组中每个元素进行按位与运算,判断是否为 0,如果为0则将这个元素归入到数组1, 否则归入到数组2,这样每个数组都有一个只出现一次的元素,而且每个数组中除了这个元素外其他元素肯定都是成对出现的。
4. 让num1[0] 对数组1的每个元素分别做异或运算,最终 num1[0] 中的元素肯定就等于 A 或 B 中的一个;同理,让num2[0] 对数组2中的每个元素分别做异或运算,最终num2[0] 中的值肯定是 A 或 B 中剩下的那个。
这里做了一个小优化:把步骤 4 和 步骤 3合并,两个操作同时进行,也就是在 让 index 与原数组中元素进行按位与运算后不将数存入数组1或者数组 2,而是直接让 num1[0] 或 num2[0] 与该数做异或运算,这样最后num1[0] 和 num2[0]就能分别存储 A 或 B 中的一个。
1 //num1,num2分别为长度为1的数组。传出参数 2 //将num1[0],num2[0]设置为返回结果 3 import java.util.Arrays; 4 public class Solution { 5 public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { 6 int xor = 0; 7 for(int i = 0; i < array.length;i++){ 8 xor ^= array[i]; 9 } 10 // 求出第一个 1 的位置 11 int index = 1; 12 while((index & xor) == 0) 13 index <<= 1; 14 // 3/4. 15 for(int i = 0; i < array.length; i++){ 16 if((index & array[i]) != 0) 17 num1[0] ^= array[i]; 18 else 19 num2[0] ^= array[i]; 20 } 21 22 } 23 }