public class Solution { public void FindNumsAppearOnce(int[] array, int[] num1, int[] num2) { int length = array.length; if(length == 2){ num1[0] = array[0]; num2[0] = array[1]; return; } int bitResult = 0; for(int i = 0; i < length; ++i){ bitResult ^= array[i]; } int index = findFirst1(bitResult); for(int i = 0; i < length; ++i){ if(isBit1(array[i], index)){ num1[0] ^= array[i]; }else{ num2[0] ^= array[i]; } } } private int findFirst1(int bitResult){ int index = 0; while(((bitResult & 1) == 0) && index < 32){ bitResult >>= 1; index++; } return index; } private boolean isBit1(int target, int index){ return ((target >> index) & 1) == 1; } }
# -*- coding:utf-8 -*-
"""
hashMap法
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
hashMap = {}
for i in array:
if str(i) in hashMap:
hashMap[str(i)] += 1
else:
hashMap[str(i)] = 1
res = []
for k in hashMap.keys():
if hashMap[k] == 1:
res.append(int(k))
return res
"""
class Solution:
def FindNumsAppearOnce(self, array):
if not array:
return []
# 对array中的数字进行异或运算
tmp = 0
for i in array:
tmp ^= i
# 获取tmp中最低位1的位置
idx = 0
while (tmp & 1) == 0:
tmp >>= 1
idx += 1
a = b = 0
for i in array:
if self.isBit(i, idx):
a ^= i
else:
b ^= i
return [a, b]
def isBit(self, num, idx):
"""
判断num的二进制从低到高idx位是不是1
:param num: 数字
:param idx: 二进制从低到高位置
:return: num的idx位是否为1
"""
num = num >> idx
return num & 1
/** * 数组中有两个出现一次的数字,其他数字都出现两次,找出这两个数字 * @param array * @param num1 * @param num2 */ public static void findNumsAppearOnce(int [] array,int num1[] , int num2[]) { if(array == null || array.length <= 1){ num1[0] = num2[0] = 0; return; } int len = array.length, index = 0, sum = 0; for(int i = 0; i < len; i++){ sum ^= array[i]; } for(index = 0; index < 32; index++){ if((sum & (1 << index)) != 0) break; } for(int i = 0; i < len; i++){ if((array[i] & (1 << index))!=0){ num2[0] ^= array[i]; }else{ num1[0] ^= array[i]; } } } /** * 数组a中只有一个数出现一次,其他数都出现了2次,找出这个数字 * @param a * @return */ public static int find1From2(int[] a){ int len = a.length, res = 0; for(int i = 0; i < len; i++){ res = res ^ a[i]; } return res; } /** * 数组a中只有一个数出现一次,其他数字都出现了3次,找出这个数字 * @param a * @return */ public static int find1From3(int[] a){ int[] bits = new int[32]; int len = a.length; for(int i = 0; i < len; i++){ for(int j = 0; j < 32; j++){ bits[j] = bits[j] + ( (a[i]>>>j) & 1); } } int res = 0; for(int i = 0; i < 32; i++){ if(bits[i] % 3 !=0){ res = res | (1 << i); } } return res; }
class Solution { public: void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) { if(data.size() < 2) return ; int myxor = 0; int flag = 1; for(int i = 0 ; i < data.size(); ++ i ) myxor ^= data[i]; while((myxor & flag) == 0) flag <<= 1; *num1 = myxor; *num2 = myxor; for(int i = 0; i < data.size(); ++ i ){ if((flag & data[i]) == 0) *num2 ^= data[i]; else *num1 ^= data[i]; } } };
public class Solution { public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { int num=0; for(int i=0;i<array.length;i++){ num^=array[i];//所有数异或,结果为不同的两个数字的异或 } int count=0;//标志位,记录num中的第一个1出现的位置 for(;count<array.length;count++){ if((num&(1<<count))!=0){ break; } } num1[0]=0; num2[0]=0; for(int i=0;i<array.length;i++){ if((array[i]&(1<<count))==0){//标志位为0的为一组,异或后必得到一个数字(这里注意==的优先级高于&,需在前面加()) num1[0]^=array[i]; }else{ num2[0]^=array[i];//标志位为1的为一组 } } } }
//异或的方法 class Solution { public: void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) { int diff=accumulate(data.begin(),data.end(),0,bit_xor<int>()); diff&=-diff; //即找到最右边1-bit *num1=0;*num2=0; for (auto c:data) { if ((c&diff)==0) *num1^=c; else *num2^=c; } } }; //哈希表 class Solution { public: void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) { unordered_map<int, int> map; for (int i = 0; i < data.size(); i++) map[data[i]]++; vector<int>v; for (int i = 0; i < data.size(); i++) if (map[data[i]]== 1) v.push_back(data[i]); *num1 = v[0]; *num2 = v[1]; } };
import java.util.ArrayList; public class Solution { public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { ArrayList<Integer>list=new ArrayList<Integer>(); for(int i=0;i<array.length;i++) { if(!list.contains(array[i])) list.add(array[i]); else list.remove(new Integer(array[i])); } if(list.size()>1) { num1[0]=list.get(0); num2[0]=list.get(1); } } }
//最简短的代码。基本思想是将这个数组一分为二,而这个分法是有讲究的: //必须将这两个不同的数字分到两个不同数组里,这可以根据这两个数中任意不同的位来划分。 class Solution { public: void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) { int Xor = 0; for(int i = 0; i < data.size(); ++i) Xor ^= data[i]; //int split = Xor&(Xor-1)^Xor; //找到两个只出现一次的数字的第一个位值不同的位置 int split = Xor&~(Xor - 1); for(int i = 0; i < data.size(); ++i){ if(data[i]&split) *num1 ^= data[i]; else *num2 ^= data[i]; } } };
class Solution { public: void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) { set<int> save; set<int>::iterator iter; for (int i = 0; i < data.size(); i++){ if (save.find(data[i]) == save.end()) save.insert(data[i]); else{ iter = save.find(data[i]); save.erase(iter); } } iter = save.begin(); *num1 = *iter; *num2 = *(++iter); } };
//num1,num2分别为长度为1的数组。传出参数 //将num1[0],num2[0]设置为返回结果 import java.util.*; import java.util.Map.Entry; public class Solution { public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { if(array==null&&array.length<=1){ num1[0]=num2[0]=0; return; } HashMap<Integer,Integer> map = new HashMap<>(); for(int i=0;i<array.length;i++){ if(map.containsKey(array[i])){ map.put(array[i],2); } else{ map.put(array[i],1); } } num1[0]=0; for(Entry entry:map.entrySet()){ if((Integer)entry.getValue()==1){ if(num1[0]==0){ num1[0]=(Integer)entry.getKey(); }else{ num2[0]=(Integer)entry.getKey(); } } } } }
一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
一种简单的思路,可以想到用HashSet
这种数据结构来存,重复的就立即剔除,剩下的就是不重复的两个数字,将其取出即可。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.*;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
HashSet<Integer> set = new HashSet<>();
for(int i=0;i<array.length;i++){
if(!set.isEmpty() && set.contains(array[i])){
set.remove(array[i]);
}else{
set.add(array[i]);
}
}
//这边处理的不够好
ArrayList<Integer> list = new ArrayList<>();
if(set.size() == 2){
for(Integer i:set){
list.add(i);
}
}
num1[0] = list.get(0);
num2[0] = list.get(1);
}
}
但是对于题目中说除了两个单个数字外,其他的都出现偶数次。我们需要从这句话入手,寻求更优的解决思路。
我们知道,位运算中异或的性质是:两个相同数字异或=0,不相同的话肯定不为0,一个数和0异或还是它本身。
这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:任何一个数字异或它自己都等于0 。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中抵消掉了。
有了上面简单问题的解决方案之后,我们回到原始的问题。如果能够把原数组分为两个子数组。在每个子数组中,包含一个只出现一次的数字,而其它数字都出现两次。如果能够这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字了。
我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其它数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0 ,也就是说在这个结果数字的二进制表示中至少就有一位为1 。
我们在结果数字中找到第一个为1 的位的位置,记为第N 位。现在我们以第N 位是不是1 为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N 位都为1 ,而第二个子数组的每个数字的第N 位都为0 。
现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其它数字都出现了两次。因此到此为止,所有的问题我们都已经解决。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
//【解决思路】:从简单的场景想起,假设一个数组中只有一个独特元素,其他出现次数都为2
//如何快速找出这个独特元素呢?那就是从头到尾两两异或,由于相同的数异或为0,则认为是抵消
//一直到最后,结果必然就是这个独特元素
//那么找出两个来也是这个思路,核心就是要将这两个独特的数分离开,下面详细介绍
if(array == null || array.length <= 1){
num1[0] = num2[0] = 0;
return;
}
//整个数组从头两两异或,最终的结果必然是两个不同数字的异或结果
//因为相同的数字两两异或之后为0
//0和任意一个数异或还是这个数本身
int len = array.length, index = 0, sum = 0;
for(int i = 0; i < len; i++){
sum ^= array[i];
}
//java中int类型占4个字节,即32个bit
//从左开始找到这个异或结果第一个为1的索引
while((sum&1) == 0 && index < 32){
sum = sum >> 1;
index++;
}
//以这个索引处是否为1作为判定标准,就将两个不同的数分离开了
//下面就是分两批不停地疑惑,就会得到这两个不同的数
for(int i = 0; i < len; i++){
//这样就可以分别找到index处为1的独特解以及为0的独特解
if(isBit(array[i],index)){
num1[0] ^= array[i];
}else{
num2[0] ^= array[i];
}
}
}
//判断num的index(从左往右看)是否为1
private boolean isBit(int num,int index){
num = num >> index;
if((num & 1) == 1){
return true;
}else{
return false;
}
}
}
利用set解决问题,简单明了
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
HashSet<Integer> set = new HashSet<Integer>();
for (int i = 0;i < array.length;i++){
if(!set.add(array[i])){
set.remove(array[i]);
}
}
Object[] temp =set.toArray();
num1[0] = (int) temp[0];
num2[0] = (int) temp[1];
}
class Solution { public: //思路:用异或的特性,A^A=0 0^X=X;以及异或的交换律特性。 void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) { int x=0,len=data.size(); for(int i=0;i<len;++i) x^=data[i];//x保存所有元素异或的结果 int n=1; while((x & n)==0)//找出最右边第1个不为0的位置 n=n<<1; int x1=0,x2=0; for(int i=0;i<len;++i) if(data[i] & n)//根据第一个不为0的位置重新将数组进行划分 x1^=data[i]; else x2^=data[i]; *num1=x1; *num2=x2; return ; } };
//使用堆栈来做辅助功能,将数组先排序,依次入栈,每一次数组入栈时和当前堆栈的栈头比较,如果当前堆栈为空,就入栈,如果和当前栈头的元素相同就出栈,当数组中左右元素都入栈完毕,那么当前栈中剩余的2个元素就是只出现一次的两个元素
public static void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
Arrays.sort(array);
Stack<Integer> stack = new Stack<Integer>();
int len = array.length;
if(array == null){
num1[0] = 0;
num2[0] = 0;
}
for(int x = 0;x<len;x++){
if(stack.isEmpty()){
stack.push(array[x]);
}else{
if(stack.peek() == array[x])
stack.pop();
else
stack.push(array[x]);
}
}
num1[0] = stack.pop();
num2[0] = stack.pop();
}
}
import java.util.*; public class Solution { public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { Queue<Integer> arr = new LinkedList<Integer>(); HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); for (int i = 0; i < array.length; i++) { if (!map.containsKey(array[i])) { map.put(array[i], 1); } else { map.put(array[i], map.get(array[i]) + 1); } } for (int i = 0; i < array.length; i++) { if (map.get(array[i]) == 1) { arr.add(array[i]); } } num1[0] = arr.poll(); num2[0] = arr.poll(); System.out.println(num1[0]); System.out.println(num2[0]); } }
}
算法的精妙之处:异或。相同的数异或之后为0,所有数组中两两相同的数异或之后,抵消为0 public class Solution { public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) { if (array == null || array.length < 2) return; int resultExclusiveNor = 0; for (int item : array) resultExclusiveNor ^= item; int firstIndex = findFirstIndex(resultExclusiveNor); num1[0]=0; num2[0]=0; for(int item:array){ if(isBit1(item,firstIndex)) num1[0]^=item; else num2[0]^=item; } } // 二进制数 从右往左 找到第一个 "1" public int findFirstIndex(int n) { int index = 0; while ((1 & n) == 0 && index < 32) { n = n >> 1; index++; } return index; } //判断这个数的二进制形式从左到右index位是否为"1" public boolean isBit1(int num, int index) { boolean check = false; num = num >> index; if ((num & 1) == 1) check = true; return check; } }
class Solution { public: void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) { if(data.size()<2) return ; int size=data.size(); int temp=data[0]; for(int i=1;i<size;i++) temp=temp^data[i]; if(temp==0) return ; int index=0; while((temp&1)==0){ temp=temp>>1; ++index; } *num1=*num2=0; for(int i=0;i<size;i++) { if(IsBit(data[i],index)) *num1^=data[i]; else *num2^=data[i]; } } bool IsBit(int num,int index) { num=num>>index; return (num&1); } };
可以用位运算实现,如果将所有所有数字相异或,则最后的结果肯定是那两个只出现一次的数字异或的结果,所以根据异或的结果1所在的最低位,把数字分成两半,每一半里都还有只出现一次的数据和成对出现的数据
这样继续对每一半相异或则可以分别求出两个只出现一次的数字