数组中唯一出现一次的一个,两个,三个数,其余数都是偶数次出现(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);
	}
}


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值