数组中唯一出现一次的一个,两个,三个数,其余数都是偶数次出现(java版本)

首先在leetcode上面有这样类似的题,做法大致类似

1,首先是只出现一次的一个数

比较简单,直接全部亦或值就得到了

//只出现一次的一个数
  public static int singleNumber1(int[] A) {
        int res=0;
       for(int i=0;i<A.length;i++)
         res^=A[i];
         return res;

    }

2,只出现一次的两个数

则所有的值亦或肯定不为0,设最后的抑或结果为M,找到M从低到高为最先为1的位置,然后根据所有数在该位置为0或者1,分为两组,这样,两个不同的数就被分到两个不同的组,单独对组亦或就得到这两个数

这里由于要移位,所以有个移位函数findOne,函数的主要作用是找出最低位的1,并将数转化为0000010000的形式,例如将8(二进制为00001000-》00001000,假定是8位)

//找到最低位的1,然后转化为返回000010000这样的形式
  public static int findOne(int sum){
 // 法一:可以返回的要位移的位数
	 /* int count=0;
	  while(((sum>>count)&1)!=1){
		  count++;
	  }
     return count;*/
	//  法二
	return sum&~(sum-1);
	//法三:
	// return sum&(-sum);

  }
 //只出现一次的两个不相等的数
  public static  int[] singleNumber2(int[] A) {
	  int sum=singleNumber1(A);
	  int []res=new int[2];
	  int temp=findOne(sum);
	 for (int i = 0; i < A.length; i++) {
		if((A[i]&temp)!=0){
			res[0]^=A[i];
		}else
			res[1]^=A[i];
	}
	  return res;
  }

3,只出现一次的三个数

对于这种情况比较复杂,前面的两张情况都是考虑数组所有元素的亦或值,根据亦或值的最低位1来进行判定,但是三个数的话,这三个数的亦或值可能为0,可能不为0,所以仅仅根据所以亦或值的最低位1来判断会分好多种情况,所以换个方法。

假定这三个数是a,b,c;x=a^b^c(其他的偶数次的都亦或为0,不管了),

容易证明x不与a,b,c中的任何一个相等(反证,x=a,则x=a^b^c推出b^c=0,推出b=c,矛盾,其他同理)

且a^x b^x c^x都不为0

在上面第2种情况中,介绍了函数findOne(),为了方便下面都简称为f()

考虑f(a^x)^f(b^x)^f(c^x),其他的data^x  都是偶数次

当data不为0的时候,f(data)中只有一位为1,其他位为0,所以上面三个f()值亦或肯定不为0(考虑三个数的1在同一位置,结果不为0;三个1有两个在同一位置,结果还是不为0,三个1互在不同位置,亦或值还是不为0)

则f(a^x)^f(b^x)^f(c^x)结果肯定是非0,f(a^x)^f(b^x)^f(c^x)中考虑最低位(假定是M位置)的1,然后可用m=f(a^x)^f(b^x)^f(c^x)(0000100000形式)

于是f(x^a)^f(x^b)^f(x^c)的结果的二进制中至少有一位是1。假设最后一位是1的位是第m位。那么x^a、x^b、x^c的结果中,有一个或者三个数字的第m位是1。

假若是三个1,a^x
b^x c^x 在m位置都是1,则a b c 在m位置与x的m位相反,a b c 在m位的数相同

1,a b c 在m位置都是0,则x在m位置也是0,则a^x b^x c^x在m处也是0,与假设矛盾

2,a b c 在m位置都是1   则x在m处也是1,a^x b^x c^x在m处就是0,与假设矛盾

所以不可能在m处是三个1

所以a^x
b^x c^x 在m处只有一个数字为1,根据m=f(a^x)^f(b^x)^f(c^x)(0000100000形式)进行分组,首先找到为1的那个单独在一组,然后从所有数中排除出去,然后剩下的数组中就剩下两个唯一的数了,用第二种情况就oK了,记得所求的是a^x b^x c^x,最后的结果要再亦或一个x

代码如下:

  //只出现一次的三个不相等的数
  public static  int[] singleNumber3(int[] A) {
	  int[] res=new int[3];
	  int[] A1=new int[A.length-1];
	  int x=singleNumber1(A);
	  int m=0;
	  int[] value=new int[2];
	for (int i = 0; i < A.length; i++) {
		A[i]=A[i]^x;
		m^=findOne(A[i]);
	}

	m=findOne(m);
	//再根据A[i]的第m位是否为1,分组(1,0,0)
	for (int i = 0; i < A.length; i++) {
		if((A[i]&m)!=0){
			res[2]^=A[i];
		}
	}
	//移除那个数
	for (int i = 0,j=0; i < A.length; i++) {
		if(A[i]!=res[2])
			A1[j++]=A[i];
	}
	res[2]=res[2]^x;
	value=singleNumber2(A1);
	for (int i = 0; i < 2; i++) {
		res[i]=value[i]^x;
	}
	  return res;
  }

主程序测试代码;

public class test10 {
	public static void main(String[] args) {
	int a[]=singleNumber3(new int[]{1, 1, 2, 2, 5,9,12,-4,-4});
	for (int i : a) {
		System.out.println(i);
	}
}
时间: 2024-08-24 18:14:26

数组中唯一出现一次的一个,两个,三个数,其余数都是偶数次出现(java版本)的相关文章

算法题1 数组中唯一出现1次的数|唯一出现2次的数

题目描述:一个整型数组里除了1个数字之外,其他的数字都出现了两次,请写程序找出这个只出现一次的数字.要求时间复杂度是 O(n),空间复杂度是 O(1),例如输入数组{2, 4, 3, 3, 2, 5 },因为只有 4 这个数字只出现一次,其他数字都出现了两次,所以输出 4 思路分析:本题以及延伸题目,在剑指offer上有详细描述.利用异或的特性,x^y^x=y^x^x=y.对数组所有元素一次进行异或操作,最终得到的值就是那个只出现一次的数字 代码 1 int FindOnceNum(int ar

找出数组中唯一的重复元素

[问题] 1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次. 每个数组元素只能访问一次,设计一个算法,将它找出来:不用辅助存储空间,能否设计一个算法实现? [代码] #include <stdio.h> #include <stdlib.h> #include <string.h> /*根据异或法的计算方式,每两个相异的数执行异或运算之后,结果为1: 每两个相同的数异或之后,结果为0,任何数与0异或,结果仍为自身. 所以数组a[N]

《剑指offer》第五十六题II:数组中唯一只出现一次的数字

// 面试题56(二):数组中唯一只出现一次的数字 // 题目:在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次.请 // 找出那个吃出现一次的数字. #include <cstdio> #include <exception> int FindNumberAppearingOnce(int numbers[], int length) { if (numbers == nullptr || length <= 0) throw new std::exceptio

找到数组中唯一重复的数

#include<iostream> #include<algorithm> #include<numeric> using namespace std; int helper1(int a[],int n) { int sum = accumulate(a,a+n,0); int sum2 = n*(n-1)/2; return sum-sum2; } int helper2(int a[],int n) { int bitor = a[0]^0; for(int i

一个数组中找到满足和为sum的两个数

如果考虑hashmap直接O(n)的速度, 如果不行,就先排序,两头指针很好推理,关键是 a[beg] +a[end]>sum,意思就是说a[end]太大了,最小的数的都不满足,所以排除a[end] 绝知此事要躬行 #include<iostream>#include<algorithm>using namespace std; bool find(int *a,int sum,int len,int &ans1,int &ans2){    int *beg

4.产生10个1-100的随机数,并放到一个数组中 (1)把数组中大于等于10的数字放到一个list集合中,并打印到控制台。 (2)把数组中的数字放到当前文件夹的numArr.txt文件中

package cn.it.text; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; /* * 4.产生10个1-100的随机数,并放到一个数组中 (1)把数组中大于等于10的数字放到一个list集合中,并打印到控制台. (2)把数组中的数字放到当前文件夹的number.txt文件中 */ public class Test4 { public static void main

找出其他数出现两次的数组中唯一一个出现一次的数

#include <stdio.h> #include <stdlib.h> #include <malloc.h> /* 有101个整数, 其中有50个数出现了两次, 1个数出现了一次, 找出出现了一次的那个数. */ /* 思路: 出现两次的数进行异或为0, 故将整个数组的数进行异或, 则得到此只出现一次的那个数 */ int isOdd(int num); int getOne(int *arr, int length); int main(){ int numb

寻找数组中唯一只出现1次的数

一个数组中除开唯一一个元素只出现一次,其余的元素均出现三次. 位运算,看不明白,留着以后看吧. 1 #include <stdio.h> 2 #include <algorithm> 3 using namespace std; 4 #define maxn 1000 5 int a[maxn],n; 6 7 int main() 8 { 9 while(scanf("%d",&n), n) 10 { 11 int one = 0, two = 0, t

1.2找数组中唯一成对的那个数

题目介绍 1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素重复,其它均只出现一次.每个数组元素只能访问一次,设计一个算法,将它找出来:不用辅助空间,能否实现? 题目解法 思路 题目的意思是有一个数组,里面的数字是唯一的1~1000,但是其中有两个数字重复,所以是1001的数组,假设这个重复的数字是K,用数(1~K~1000)^ 数组(1~K~K~1001)就得到 K. K^K=0,K^0=A 核心代码 int N=1001; int x1=0; //数(1~K~100