一个数组中,存在两个只出现一次的数字,其余的数字均出现两次。要求在时间复杂度o(n),空间复杂度为o(1)的情况下找出这两个数字

一问题描述
    一个数组中,存在两个只出现一次的数字,其余的数字均出现两次。要求在时间复杂度o(n),空间复杂度为o(1)的情况下找出这两个数字。
 
二 问题分析
     此题实际考察了,对位操作的理解。首先进行简化,考虑只有一个数组中,只存在出现了一次的一个数字,其余数字在数组中出现两次,试
     找出这个数字。

 三 解决方案
    首先 回忆 异或操作,任意数字与自身相异或,结果都为0.
   还有一个重要的性质,即任何元素与0相异或,结果都为元素自身。

   得到一个数字的某一位:第0位:a&1;第1位:a&(1<<1)...

   特别注意:在判断a&b是否为0时,这样写是不对的:if(a&b==0)。因为==的优先级高于&,相当于a&(b==0)。而应该写成:if( (a&b)==0 ) 或if(!(a&b))


   解决方案:
  1   从数组的起始位置开始,对元素执行异或操作,则最后的结果,即为此只出现了一次的元素。
 
  2  题目中,数组中存在两个不同的元素,若是能仿造上述的解决方案,将两个元素分别放置在两个数组中,然后分别对每个数组进行异或操作,
     则所求异或结果即为所求。
  
  3  首先对原数组进行全部元素的异或,得到一个必然不为0的结果,然后判断该结果的2进制数字中,为1的最低的一位。
      然后根据此位是否为1 ,可以把原数组分为两组。则两个不同的元素,必然分别在这两个数组中。

 4  然后对两个数组,进行异或操作,即可得到所求。

四 代码示例

#include <iostream>
  
using namespace std ;
  
const int N = 10 ;
  
  
int getSingle(int * a) //获取全部元素的异或结果 
  {
      
if(!a)
        
return -1;
        
     
int sum = a[0] ; 
     
     
for(int i = 1; i < N; i++)
       sum 
^= a[i] ;
       
     
return sum ;   
  }

  
  
  
int getTwo(int * a ,int & one , int & two , int sum) //求数组中两个不同的元素 
  {
      unsigned 
int flag = 1;
      
while(flag) //求异或结果,最低的为1的二进制位,根据此位是否为1,将元素分为两组,两个不同的元素,在此位必然,一个为1,一个为0 
      {
        
if(flag&sum)
           
break;
        flag 
= flag << 1 ;        
      }

      
//下面将flag与每个元素相求与,根绝结果是否为1,将其化为两个数组
      
//分别计算每个数组的异或结果,并将结果,存储分别存储在one和two中。
      one = two = 0 ;//0与任何数异或都为其自身,所以初始化的时候,应该初始化为0 
      for(int i = 0 ; i < N ;i++ )
      
{
        
if(a[i] & flag) 
        
{
                one 
^= a[i] ;
        }

        
else
        
{
             two 
^= a[i] ;
        }
        
      }

      
  }

  
  
int main()
  
{
      
int a[N] = {3 , 5 ,8 , 8 , 5 , 3 ,1 ,4 ,4,10} ;   
      
int single = getSingle(a) ;
      
int one = 0 ;
      
int two  = 0;
      getTwo(a ,one , two ,single) ;
      cout
<<single<<" "<<one<<" "<<two<<endl ;
     system(
"pause") ;
     
return 0 ;    
  }


#include <iostream>
using namespace std;


int find_1(int a[],int n)
{
	int b=a[0];
	for(int i=1;i<n;i++)
	{
		b=b^a[i];
	}
	return b;
}
void find_2(int a[],int n)
{
	int one=0,two=0;
	int b=find_1(a,n);
	int flag=1;//000001  从后向前找 哪一位为1 
	while(1)
	{
		if((b&flag)!=0)
			break;
		flag=flag<<1;
	}
	for(int i=0;i<n;i++)
	{
		if(a[i]&flag)
		{
			one=one^a[i];
		}
		else
		{
			two=two^a[i];
		}
	}
	cout<<endl<<one<<' '<<two<<endl;
}
void main()
{
	int a[]={11,4,11,2,1,5,2,4,9,5};
	int n=sizeof(a)/4;
	find_2(a,n);
	system("pause");
}



找出一个整型数组中出现奇数次的两个数,可以利用异或运算的性质来解决。我们知道,对于任何一个数x,x与自身异或的结果为0,x与0异或的结果为x。基于这个性质,我们可以将数组中所有的数进行异或运算,最终得到的结果就是两个出现奇数次的数的异或结果。 具体步骤如下: 1. 遍历整型数组,将所有的数进行异或运算,最终得到的结果为xor_result,即两个出现奇数次的数的异或结果。 2. 找到xor_result中任意一个为1的位,可以使用位运算的技巧。比如可以使用 xor_result & -xor_result 来得到xor_result最低位的1。 3. 将整型数组分为组,一组是在该位上为1的数,另一组是在该位上为0的数。 4. 分别对这组数进行异或运算,得到的结果就是两个出现奇数次的数。 下面是一个实现这个算法的示例代码: ```python def find_two_numbers(nums): xor_result = 0 for num in nums: xor_result ^= num # 找到xor_result中任意一个为1的位 bit = xor_result & -xor_result num1, num2 = 0, 0 for num in nums: if num & bit: num1 ^= num else: num2 ^= num return [num1, num2] # 测试 nums = [2, 4, 6, 8, 5, 4, 2, 6] result = find_two_numbers(nums) print(result) ``` 运行以上代码,将会输出两个出现奇数次的数:[5, 8]。 这个算法的时间复杂度为O(n),其中n是数组的长度。希望这个解决方案对你有帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值