• 【剑指offer】数字数组中只出现一次(2)


    转载请注明出处:http://blog.csdn.net/mmc_maodun/article/details/27800577


        题目:一个int数组中有三个数字abc仅仅出现一次,其它数字都出现了两次。

    请找出三个仅仅出现一次的数字。

        上篇博文中我们求的是两个仅仅出现一次的数字。且时间复杂度为O(n),这次是三个,能够相同考虑将数组先分成两个子数组,求出当中一个仅仅出现一次的数字,而后再将还有一个子数组分成两个子数组。再分别求这两个仅仅出现一次的数字。何海涛的博客给的就是这样的思路,并给出了具体的证明过程,详见:http://zhedahht.blog.163.com/blog/static/25411174201283084246412/,但该方法真的要自己去想,非常难想到,并且假设是面试的话。非常难给面试官讲懂。以下介绍第二种方法。该方法具一定的通用性,对于2个。3个出现一次的数字这类的问题,都能够依照该思路去求解,仅仅是时间复杂度可能要略微大些,为O(8*sizeof(int)*n),8*sizeof(int)事实上即使int的位,在一般的32位系统中,它为32,而n自然就是数组的长度了。

        该方法的思路例如以下:

        首先因为有3个数字出现一次,其它的都出现两次。所以n肯定为奇数,该方法通过扫描整数的每一位来逐个推断。

        再看这3个仅仅出现一次的数字,他们的bit位肯定不可能全部相同。也就是说。尽管有些bit位上的数可能相等。但肯定至少存在某一个bit位,这三个数中,有两个数的该bit位为1,一个数的该bit位为0。或者两个数的该bit位为0。一个数的该bit位为1。

    我们能够通过扫面int的全部bit位,扫描每一个bit位的时候。遍历数组,假设能找出符合上面条件的,我们就能够找出当中的一个仅仅出现一次的数字,该数字与另外两个仅仅出现一次的数的bit位不同。找到一个之后,就能够将其与数组的最后一个元素交换。再在前面n-1个数中找出另外两个就能够了。方法的话。能够直接用上篇博文中介绍的方法,也能够用该博文介绍的思路。

        以下要来看下假设找出这个与另外两个数的该bit位不同的数。

        先看第一种情况。假设a,b,c三个数中,有两个该bit位为0,还有一个为1,我们遍历数组,分别统计该数组元素中该bit位为1和0的元素个数。分别设为count1和count0,并同一时候将全部该bit位为1的元素异或,全部该bit位为0的元素异或。得到的结果分别设为temp1和temp0。假设count1为奇数。则可能有两种情况,a,b,c三个数的该bit位全为1。或者有两个为0,一个为1,假设有temp0==0,则说明是前一种情况(a,b。c的该bit位全为1的话,全部该bit位为0的每一个元素出现了两次。因此异或后的结果为0),此时没法找出当中的一个数。则直接跳到下次循环,继续推断下一个bit位,假设temp0。=0。则说明是后一种情况(说明该比bt位为0的元素异或后没有全然抵消,则说明有一个元素是仅仅出现一次的)。此时当中一个仅仅出现一次的数字就是temp0(反复的元素异或后都抵消了)。

        第二种情况,是两个该bit位为1。还有一个为0的情况,分析思路与上面的相似。

        非常明显,这样的扫描每一个bit位来进行推断的思路能够解决整个的这一类问题,自然也能够求出上篇博文中两个仅仅出现一次的数字。具体思路不再给出,基本的推断根据,一般都是bit位为1或0的数字的个数的奇偶。bit位为1的元素异或,bit位为0的元素异或后的结果是否为0的推断。

        以下给出这道题目的完整代码:

    #include<stdio.h>
    
    /*
    通过扫面每一位,先找出一个仅仅出现一次的数
    */
    int FindOneNumAppearOnce(int *arr,int len)
    {
    	int count1 = 0;	//某一位上1的个数
    	int count0 = 0; //某一位上0的个数
    	int temp1 = 0;	//某一位为1的全部数相异或的结果
    	int temp0 = 0;	//某一位为0的全部数相异或的结果
    
    	int i,j;
    	for(i=0;i<8*sizeof(int);i++)	//循环计算每一位的以上四个数据
    	{
    		count1 = count0 = temp1 = temp0 = 0;//每次计算下一位时清零
    		for(j=0;j<len;j++)
    		{	
    			//每次向左移一位进行计算
    			if(arr[j] & (1<<i))	//该位为1时
    			{
    				temp1 ^= arr[j];
    				count1++;
    			}
    			else
    			{
    				temp0 ^= arr[j];
    				count0++;
    			}
    		}
    
    		if(temp1 & 1)	//某位上有奇数个1
    		{
    			if(temp0 == 0)	//此时3个不同数的该位都为1
    				continue;
    			else			//此时3个不同数的该位有1个1,2个0
    				return temp1;
    		}
    		else	//某位上有偶数个1
    		{
    			if(temp1 == 0)	//此时3个不同数的该位都为0
    				continue;
    			else			//此时3个不同数的该位有1个0,2个1
    				return temp0;
    		}
    	}
    }
    
    /*
    返回num的最低位的1。其它各位都为0
    */
    int FindFirstBit1(int num)
    {
    	//二者与后得到的数,将num最右边的1保留下来,其它位的全部置为了0
    	return num & (-num);
    }
    
    /*
    推断data中特定的位是否为1。
    这里的要推断的特定的位由res确定,
    res中仅仅有一位为1。其它位均为0。由FindFirstBit1函数返回。
    而data中要推断的位便是res中这唯一的1所在的位
    */
    bool IsBit1(int data,int res)
    {
    	return ((data&res)==0) ?

    false:true; } void FindTwoNumsAppearOnce(int *arr,int len,int *num1,int *num2) { int i; int AllXOR = 0; //全部异或 for(i=0;i<len;i++) AllXOR ^= arr[i]; int res = FindFirstBit1(AllXOR); *num1 = *num2 = 0; for(i=0;i<len;i++) { if(IsBit1(arr[i],res)) *num1 ^= arr[i]; else *num2 ^= arr[i]; } } /* 交换两个int变量 */ void Swap(int *a,int *b) { if(*a != *b) { *a ^= *b; *b ^= *a; *a ^= *b; } } /* 找出这三个仅仅出现一次的数字 */ void FindThreeNumsAppearOnce(int *arr,int len,int *num1,int *num2,int *num3) { if(arr==NULL || len<3) return; *num1 = FindOneNumAppearOnce(arr,len); //找到第一个找出的数字,并与最后一个元素交换,便于接下来剩下的两个数字 int i; for(i=0;i<len;i++) if(*num1 == arr[i]) break; Swap(&arr[i],&arr[len-1]); FindTwoNumsAppearOnce(arr,len-1,num2,num3); } int main() { static int arr[1000000]; int n; while(scanf("%d",&n) != EOF) { int i; for(i=0;i<n;i++) scanf("%d",arr+i); int num1,num2,num3; FindThreeNumsAppearOnce(arr,n,&num1,&num2,&num3); printf("%d %d %d ",num1,num2,num3); } return 0; }

        測试结果:



    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    2.列出目录内容
    mongo2csv
    Python-uiautomator使用说明文档
    uiautomator设备和选择器~Python详解
    hadoop1.2开发环境搭建
    postgresql 查询某一个表中的所有字段
    VolgaCTF 2020 Qualifier Library
    VolgaCTF 2020 Qualifier Newsletter
    2019-2020-2 网络对抗技术 20175211 Exp3 免杀原理与实践
    BJDCTF 2nd Writeup
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4715077.html
Copyright © 2020-2023  润新知