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所在的最低位,把数字分成两半,每一半里都还有只出现一次的数据和成对出现的数据
这样继续对每一半相异或则可以分别求出两个只出现一次的数字