首页 > 试题广场 >

数组中只出现一次的数字

[编程题]数组中只出现一次的数字
  • 热度指数:450661 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
推荐
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所在的最低位,把数字分成两半,每一半里都还有只出现一次的数据和成对出现的数据
 这样继续对每一半相异或则可以分别求出两个只出现一次的数字
编辑于 2015-08-18 23:24:57 回复(77)
import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int i = 0; i < array.length; i++){
            if(!map.containsKey(array[i])){
                map.put(array[i] , i);
            }else{
                map.remove(array[i]);
            }
        }
        Object[] a = map.keySet().toArray();
        num1[0] = (int) a[0];
        num2[0] = (int) a[1];
    }
}

发表于 2023-03-08 19:55:23 回复(0)
import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if (array == null) {
            return;
        }
        HashSet<Integer> set = new HashSet<>();
        for (Integer num : array) {
            if (set.contains(num)){
                set.remove(num);
            }else {
                set.add(num);
            }
        }
        int i = 0;
        for (Integer a: set) {
            if (i == 0){
                num1[0] = a;
            }else {
                num2[0] = a;
            }
            i++;
        }
    }
}

发表于 2022-05-29 10:54:50 回复(0)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public static void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        //得到两个只出现一次的数异或后的结果
        int res=0;
        for(int i:array){
            res=res^i;
        }
        //记录哪个位置出现1
        int temp=0;
        for(temp=0;temp<32;temp++){
            //计算哪一位需要用到算数右移>>,算出来的结果类似于res/2^temp;
            if(((res>>temp)&1)==1){
                break;
            }
        }
        //把数组分成两半
        for(int i:array){
            if(((i>>temp)&1)==1){
                num1[0]=i^num1[0];
            }else {
                num2[0]=i^num2[0];
            }
        }
    }
}
发表于 2022-04-05 10:44:51 回复(0)
import java.util.*;

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        HashMap<Integer,Integer> map=new HashMap<>();
        for(int i: array){
            if(!map.containsKey(i)){
                map.put(i,1);
            }else{
                map.put(i, map.get(i)+1);
            }
        }
        int[] ans=new int[2];
        int cnt=0;
         for(int i:array){
             if(map.get(i)==1){
                 ans[cnt]=i;
                 cnt++;
             }
         }
        num1[0]=ans[0];
        num2[0]=ans[1];
    }
}
位异或

import java.util.*;

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
         int eor=0;
        for(int i=0;i<array.length;i++){
            eor=eor^array[i];
        }
        int rightOne=eor&(~eor+1);
         for(int i=0;i<array.length;i++){
            if((array[i]&rightOne)!=0){
                num1[0]=num1[0]^ array[i];  //num1[0]初始为0
            }
        }
        num2[0]=eor^num1[0];
    }
}


发表于 2021-10-04 12:22:47 回复(0)
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        Arrays.sort(array);
        for(int i=0;i<array.length;i++){
            if(i+1<array.length && array[i]==array[i+1]){
                i++;
            }else if(num1[0]==0){
                num1[0]=array[i];
            }else{
                num2[0]=array[i];
            }
        }
    }

发表于 2021-02-24 22:04:08 回复(0)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int xor1 = 0;
		for (int i = 0; i < array.length; i++) {
			xor1 = xor1 ^ array[i];
		}
		// 在xor1中找到第一个不同的位对数据进行分类,分类为两个队列对数据进行异或求和找到我们想要的结果
		int index = 1;
		while ((index & xor1) == 0)
			index = index << 1;// 左移
		int result1 = 0;
		int result2 = 0;
		// 根据index的位数进行分组
		for (int i = 0; i < array.length; i++) {
			if ((index & array[i]) == 0)
				result1 = result1 ^ array[i];
			else
				result2 = result2 ^ array[i];
		}
		num1[0] = result1;
		num2[0] = result2;
    }
}

发表于 2021-01-05 15:44:06 回复(0)
直接上hashmap,遍历一遍即可。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        solve(array);
        int[] num = new int[2];
        int index = 0;
        for(Map.Entry entry:map.entrySet() ){
            if((Integer)entry.getValue()==1){
                num[index++] = (Integer)entry.getKey();
            }
        }
        num1[0] = num[0];
        num2[0] = num[1];
     }
    
    
   //想法:用hashMap来解决,依次遍历所有数组数字,存放
    
    LinkedHashMap<Integer,Integer> map = new LinkedHashMap<>();
    public void solve(int[] array){
        int len = array.length;
        
        for(int i = 0;i<len;i++){
            if(map.get(array[i])==null){
                map.put(array[i],1);
            }else{
                int temp = map.get(array[i]);
                map.put(array[i],temp+1);
            }
        }
    }
    
}

发表于 2020-11-14 17:27:33 回复(0)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if(array == null || array.length == 0)
            return;
        // 先异或一次取得结果为两个不同数得异或结果,两个相同数异或等于0了
        int length = array.length;
        int first = 0;
        for(int i = 0 ; i < length ; i++){
            first ^= array[i];
        }
        // 取first中最低位的1,可以使用lowbit
        // 这种操作会返回最后一个1及其后面所有零,如lowbit(0011 1000) = 0000 1000
        int lowbit = first & -first;
        
        // 根据lowbit分组即可,分组时直接加入异或
        int a = 0;
        int b = 0;
        for(int i = 0 ; i < length ; i++){
            if((array[i] & lowbit) == 0){
                a ^= array[i];
            }else{
                b ^= array[i];
            }
        }
        // 最终因为相同的数被分到了同一数组,同的两个数被分到了不同数组
        // 因此最终a和b数值即为所求结果
        num1[0] = a;
        num2[0] = b;
        return;
    }
}

发表于 2020-10-14 18:42:26 回复(0)
具体解析看代码的注释。
public class Solution {
    /**
    题目描述
    一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
    题解:
    因为题目提示了用位运算: 位运算中异或的性质:两个相同数字异或=0,一个数和0异或还是它本身。 
    1. 将array 的全部元素进行 异或, 如果结果temp为 0 ,表示不存在两个不同的数字,直接退出;否则进入下一步
    2. 找出 temp 的最右边的 1 所在的位置,因为异或操作“相同为0,不同为1”,如果该位置index 为 1 表示,不同的两个数字再该位置上不同;
    3. 根据 index 把 array 分成两个子数字, 其中任一个子数组有且仅有一个单一的数字,所以直接与 num1[0]/ num2[0] 进行异或,
        最终的异或结果是该单一的数字(以为成对的元素已经异或为0)
    */
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int temp = 0;
        for (int i = 0; i < array.length; i++) {
            temp ^= array[i];
        }
        // 存储 temp 的最右边的 1 所在的位置
        int index = 0;
        while ((temp & 1) == 0) {
            index++;
            // >> 带进位右移
            temp = temp >> 1;
        }
        // 根据 index 可以把 array 分为两个部分,每个部分均有一个单一的数字
        num1[0] = num2[0] = 0;
        for (int i = 0; i < array.length; i++) {
            // array[i] >> index) & 1 : 求 array[i] 数字的第 index 位的数字是1 还是 0, 以此为array 的分割条件
            if (((array[i] >> index) & 1) == 1) {
                num1[0] ^= array[i];
            }else {
                num2[0] ^= array[i];
            }
        }
    }
}


发表于 2020-10-04 23:13:59 回复(0)
import java.util.HashSet;
import java.util.Iterator;

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {

        HashSet<Integer> result = new HashSet<>();
        for(int i = 0; i < array.length; i++){
           if(result.contains(array[i])){
               result.remove(array[i]);
              continue;
           }
           result.add(array[i]);
        }
        Iterator<Integer> iterator = result.iterator();
        while(iterator.hasNext()){
            num1[0] = iterator.next();
            num2[0] = iterator.next(); 
        }
    }
}

发表于 2020-08-31 21:45:09 回复(0)
一个简单的思路:一个数第一次遇到存入 set,第二次遇到将该数从 set 中删除,最后剩下的两个就是只出现一次的。
题目保证最后有两个只出现一次的,所以最后两行就不考虑元素多或少的问题了。
import java.util.*;

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        // 一个数第一次遇到存入 set,第二次遇到将该数从 set 中删除,最后剩下的两个就是只出现一次的
        TreeSet<Integer> set = new TreeSet<>();
        for(Integer x : array) {
            if(!set.contains(x)) {
                set.add(x);
            } else {
                set.remove(x);
            }
        }
        num1[0] = set.pollFirst();
        num2[0] = set.pollFirst();
    }
}


发表于 2020-08-28 16:31:16 回复(0)
龟龟,整形数组害可以放1000000???
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int[] arr = new int[128];
        for (int i=0;i<array.length;i++) {
            if (arr[array[i]]==0) {
                arr[array[i]] = 1;
            } else {
                arr[array[i]] = 2;
            }
        }
        boolean flag = false;
        for (int i=0;i<arr.length;i++) {
            if (arr[i]==1) {
                if (!flag) {
                    num1[0] = i;
                    flag = true;
                } else {
                    num2[0] = i;
                    break;
                }
            }
        }
    }
}


发表于 2020-08-15 20:51:07 回复(1)
import java.util.HashMap;
import java.util.Set;
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
      public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();

        for (int i = 0; i < array.length; i++) {
            if (!hashMap.containsKey(array[i])){
                hashMap.put(array[i],1);
            }else {
                hashMap.put(array[i],(hashMap.get(array[i]))+1);
            }
        }
        int count = 0;
        Set<Integer> keySet = hashMap.keySet();
        for (int key : keySet) {
            if (hashMap.get(key) == 1) {
                if (count == 0) {
                    num1[0] = key;
                    count++;
                } else if (count == 1){
                    num2[0] = key;
                } else {
                    break;
                }
            }
        }
    }
}


编辑于 2020-08-14 21:49:54 回复(0)
/*思路:先排序,那么相同的两个数必定是挨着的;
判断第一个数,若和第二个不相等,第一个肯定没有重复的;
判断最后一个数,若和倒数第二个不相等,最后一个肯定没有重复的;
再循环判断第二个数到倒数第二个数,若每个数和前后两个数都不同,那么是没有重复的;
*/
import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        Arrays.sort(array);//排序,从小到大
        int[] temp=new int[2];//用于存放num1[0],num2[0]两个不重复的数
        int k=0;
        //判断第一个数是否有重复
        if(array[0]!=array[1]){
        	temp[k]=array[0];       	        	 
        	k++;
        }
        //判断最后一个数是否有重复
        if(array[array.length-1]!=array[array.length-2]){
        	temp[k]=array[array.length-1]; 
        	k++;        	
        }
        //判断第二个到倒数第二个数是否有重复
        for(int i=1;i<array.length-1;i++){            
            if ((array[i]!=array[i-1])&&(array[i]!=array[i+1])) {
            	temp[k]=array[i]; 
				k++;
			}           
        } 
        num1[0]=temp[0];
        num2[0]=temp[1];
        System.out.println(num1[0]+" "+num2[0]);
    }
}

发表于 2020-08-09 20:40:31 回复(0)

JAVA|HashSet|^

两种解题思路,不过运算时间差不多。
第一种HashSet,我们把数据挨个遍历加入HashSet,第二次遇见就删除。最后留下的就是不重复的。
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        HashSet<Integer> s = new HashSet<>();
        for(int var:array){
            if(s.contains(var)) s.remove(var);
            else s.add(var);
        }
        Iterator it = s.iterator();
        num1[0] = (int)it.next();
        num2[0] = (int)it.next();
    }
}

思路二、利用异或运算很厉害!!!!具体操作流程:

因为如果a==b a^b = 0 0^c = c

异或是同0异1!!!

因此其余数据都是重复的,我们从头到尾异或,最后的结果就是这两个不同的数据的异或结果。

然后我们再找出异或结果中的1(代表两个数据在这个位上不同,也就是一个是1一个是0)那么接着我们再按照这个将数据分为两组,一组是在该位是1一组是在该位是0,那么在进行一次全体异或,就可以得出两个数据了!!!!!!!!!!

public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
       int result=0;
		 for(int var:array) result^=var;
		 int flag = 1;
		 //从右往左找到第一个1,也就是两个位数不同的位
		 while((result&flag)==0) flag<<=1;
		 for(int var:array) {
			 if((var&flag)==0) num1[0]^=var;
			 else num2[0]^=var;
		 }
    }



发表于 2020-08-07 01:56:59 回复(0)
import java.util.*;
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果

public class Solution {
    /**
     *
     * 对于题目中说除了两个单个数字外,其他的都出现偶数次。我们需要从这句话入手,寻求更优的解决思路。
     *
     * 我们知道,位运算中异或的性质是:两个相同数字异或=0,不相同的话肯定不为0,一个数和0异或还是它本身。
     *
     * 这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:
     * 任何一个数字异或它自己都等于0 。也就是说,如果我们从头到尾依次异或数组中的每一个数字,
     * 那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中抵消掉了(异或运算的交换律,可以参考博客:
     * https://blog.csdn.net/luzhensmart/article/details/107586619)。
     *
     * 有了上面简单问题的解决方案之后,我们回到原始的问题。如果能够把原数组分为两个子数组。
     * 在每个子数组中,包含一个只出现一次的数字,而其它数字都出现两次。如果能够这样拆分原数组,
     * 按照前面的办法就是分别求出这两个只出现一次的数字了。
     *
     * 我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。
     * 因为其它数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0 ,
     * 也就是说在这个结果数字的二进制表示中至少就有一位为1 。
     *
     * 我们在结果数字中找到第一个为1 的位的位置,记为第N 位。现在我们以第N 位是不是1 为标准把原数组中的数字分成两个子数组,
     * 第一个子数组中每个数字的第N 位都为1 ,而第二个子数组的每个数字的第N 位都为0 。
     *
     * 现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其它数字都出现了两次。
     * 因此到此为止,所有的问题我们都已经解决。
     */
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {

        if(array == null ||array.length <= 1){
            num1[0] = 0;
            num2[0] = 0;
            return;
        }

        //异或运算结果
        int xorResult = 0;
        for(int i=0;i<array.length;i++){
            xorResult ^= array[i];
        }


        /**
         * int 4字节 32bit 最高位为正负数代表 计算二进制结果xorResult从左到右的顺序 第一个位置为1的位置
         * 从左开始找到这个异或结果第一个为1的索引
         */
        int index = 0;
        while((xorResult&1)==0 && index < 32){
            xorResult = xorResult >> 1;
            index++;
        }

        //以这个索引处是否为1作为判定标准,就将两个不同的数分离开了
        //下面就是分两批不停地疑惑,就会得到这两个不同的数
        for(int j=0;j<array.length;j++){
            //这样就可以分别找到index处为1的独特解以及为0的独特解
            if(targetIndexIs1(array[j],index)){
                num1[0] ^= array[j];
            }else{
                num2[0] ^= array[j];
            }
        }
    }

    /**
     * 判断num的index(从左往右看)是否为1
     */
    public boolean targetIndexIs1(int target,int index){

        target = target >> index;
        if((target&1) == 1){
            return true;
        }else{
            return false;
        }
    }

}

发表于 2020-07-26 00:15:21 回复(0)
就是大家最容易想到的解法, HashMap; 然后我看了下书上, 说是用异或,先分享下这个代码然后再试试异或:
    
import java.util.HashMap;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if( array == null || array.length <=1) return;
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i=0; i< array.length ; i++){
            if(map.containsKey(array[i])){
                map.put(array[i], 1);
            }else{
                map.put(array[i], null);
            }
        }
        boolean flag = true;
        for(Integer key : map.keySet()){
            if(map.get(key) == null){
                if(flag){
                     num1[0]= key;
                    flag = false;
                }else{
                    num2[0]= key;
                }
            }
        }
    }
}



发表于 2020-07-09 13:28:53 回复(0)
/num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
         int ret = 0;
        for (int i : array) {
            ret ^= i;
        }
        //取出某一位上不同
        num1[0] = 0;
        num2[0] = 0;
        ret &= (-ret);
        for (int i : array) {
            if ((ret & i) == 0) {
                num1[0] ^= i;
            } else {
                num2[0] ^= i;
            }
        }
    }
}

发表于 2020-06-22 09:27:56 回复(0)
我感觉自己的暴力解法,思路是清晰的,但是不过好像需要优化一下。交给各位大神吧,哈哈。
排序后的第一个数和最后一个数需要单独判断。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.Arrays;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        Arrays.sort(array);//先对数组排序
		if(array[0]!=array[1]) {//对第一个数单独判断
			num1[0] = array[0];
			for (int i = 1; i < array.length-1; i++) {
                //因为要判断当前索引是否和前后数字相等,所以 i < array.length-1
				if(array[i]!=array[i-1] && array[i]!=array[i+1]) {
					num2[0] = array[i];
				}
			}
		}else {//else后表示第一个数是重复的,继续寻找
			int index = 0;
			for (int i = 1; i < array.length-1; i++) {
				if(array[i]!=array[i-1] && array[i]!=array[i+1]) {
					num1[0] = array[i];
					index = i;//记录第一次的找到的索引,然后break
					break;
				}
			}
			for (int i = index+1; i < array.length-1; i++) {
				if(array[i]!=array[i-1] && array[i]!=array[i+1]) {
					num2[0] = array[i];
				}
			}
            //循环条件 i < array.length-1,因此不会遍历到最后一个数
            //如果循环完没有找到第2个数,那么最后一个数就是第二个数,单独判断一下。
            if(array[array.length-2]!=array[array.length-1])
				num2[0] = array[array.length-1];
		}
    }
}


发表于 2020-06-19 15:49:50 回复(0)
//暴力解法:直接将数字排序,相同的两个数必然相邻,遍历时跨度为2,遇到相邻两数不一致时,取出该数,跨度为1继续遍历
import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        Arrays.sort(array);
        int j = 0;
        int[] arr = new int[2];
        for(int i=0;i<array.length-1;i+=2){
            if(array[i]!=array[i+1]){
                arr[j++]=array[i];
                i--;
                if(j==2) break;
            }
        }
        //如果第二个数在最后面,则上述的遍历将会跳过这个数
        if(array[array.length-1]!=array[array.length-2]){
            arr[1]=array[array.length-1];
        }
        num1[0] = arr[0];
        num2[0] = arr[1];
    }
}
发表于 2020-05-29 08:07:41 回复(0)

问题信息

难度:
173条回答 159254浏览

热门推荐

通过挑战的用户