• 三个数是唯一出现的,其余的都出现偶数个,找出这三个数中。


    题目:一个数组里,除了三个数是唯一出现的,其余的都出现偶数个,找出这三个数中。

    比如数组元素为[1,1,2,2,3,3,4,5,6],只有4,5,6这三个数字是唯一出现的,我们只需要输出4,5,6中的一个就行。

    思路:

    1.这个数组元素个数一定为奇数 

    假如有103个数,则有50对数,加上三个不同的数,则这三个和50对中的任何一个都不同。

    2.因为3个数不相同, 三个数一定不可能每一bit位都相同。 如果每个bit位都相同,那么十进制的值也一定相等的。

    我们可以找到其中一个数的bit位与其他两个不同,可以把那一个数从三个数字中分出来.

    3.三个数肯定可以分到两组不同的数组里面去,基于这样的思路就可以找出这三个不同数字中的一个.

    因为一位bit位只能区分两种状态,所以根据其中一个数的bit位与其他两个不同,可以把三个数中的二个分到一组,一个分到另一组。

    下面用到异或的性质 

    1.A^A = 0 两个相同的数异或一定等于0

    2. A^0 = A 任何数和0异或都等于0本身

    比如数组元素为[1,1,2,2,3,3,4,5,6]  2的二进制是  010 ,3的二进制是011 ,如果按最后一位分,

    最后一位是0在第一组,最后一位是1在第二组,那么两个2 一定都在第一组,两个3 一定都在另二组,然后

    2^2 与3^3 的异或结果就都等于0.  0在与其他的单个数异或,结果就等于那个数了。

    将找出的数和所有数异或,相当于又放入数组一个这个数, 这时候数组中就只有两个不同的数了。

    考虑“只有两个数出现一次”的情况:可以找到一种方法,将数组划分为两部分,且让这两个数分别在不同部分,

    这样每部分所有数的异或值,恰好分别等于这两个数。一种简单的分法就是,先计算出这两个数的异或值M(等价于求数组中所有数的异或值),

    求出M值的二进制表示中的最低位1(其它位的1也可以,只不过麻烦点)在 第k位,然后根据第k位是否为1,将原数组分为两部分。

    如4,5,6的二进制分别为0100,0101,0110。因为5的的倒数第一位为1,而其他的为0,程序第一个输出的就是5了。

    程序如下:

    #include<stdio.h>
    #include<stdlib.h>
    
    #define bit(t,i) (	(t) & (1 << (i) ))
    #define N 9
    int findOneDif(int arr[], int len)
    {
    	int groupA, groupB, cntA, cntB, i, j;
    
    	for (i = 0; i <= 31; i++)
    	{
    		groupA = groupB = cntA = cntB = 0;  //全部初始化为0
    		for (j = 0; j < len; j++)
    		{
    			if ( bit(arr[j], i))  //按倒数第i位分组,第i位为1在第一组,第i位为0在倒数第二组
    			{
    				groupA ^= arr[j];
    				cntA++;
    			}
    			else
    			{
    				//groupB ^= arr[j];
    				cntB++;
    			}
    		}
    		if (cntA % 2 == 1 && groupB != 0)	
    		{
    			//A组是奇数个元素,另外两个不同的数在B组(B组的异或值就不等于零)。
    			printf("%d
    ", groupA);
    			return groupA;
    		}
    		if (cntB % 2 == 1 && groupA != 0)
    		{
    			//B组是奇数个元素,另外两个不同的数在A组(A组的异或值就不等于零)。
    			printf("%d
    ", groupB);
    			return groupB;
    		}
    	}
    }
    
    int findLastBit1(int n)
    {
    	int i;
    	for (i = 0; i < 32; i++)
    	{
    		if (n & (1 << i))
    		{
    			return i;
    		}
    	}
    }
    int main()
    {
    	int arr[] = { 1,1,2,2,3,3,4,5,6 };
    	int bit_pos,difNum, i,a_b, groupA, groupB, dist;
    	difNum = findOneDif(arr, N);     //找出一个出现一次的数,一会将找出的这个数和所有的数异或
    	a_b = 0;
    	for (i = 0; i < N; i++)			//(相当于找出的这个数在数组中出现2次)结果就等于另外两个不同的数异或,
    	{								//因为除了三个不同的数,其他的数异或结果本来就为零。
    		a_b ^= arr[i];
    	}
    	a_b ^= difNum;					//求出两个数的异或值
    	bit_pos = findLastBit1(a_b);	//找出异或结果最后一位不为1的位置,假设为k位
    	dist = (1 << bit_pos);				
    	groupA = groupB = 0;
    	for (i = 0; i < N; i++)
    	{
    		if (arr[i] == difNum)    //将数组中找的那一个不同的数排除掉
    			continue;
    		if (arr[i] & dist)       //k位为1,分第一组 
    		{
    			groupA ^= arr[i];
    		}
    		else
    		{							//k位为0,分第二组 
    			groupB ^= arr[i];
    		}
    	}
    	printf("%d %d %d
    ", groupA, groupB, difNum);<span style="white-space:pre">		</span>//分出了三个数
    	system("pause");
    }


  • 相关阅读:
    C# 正则表达式
    C# 预处理命令
    C# System.Collections
    C#文件流 System.IO和对文件的读写操作
    c# 网站发布
    C# 数据库
    c# 数据存储过程 存储函数
    insert 插入
    SVN远程管理
    【Win】印象笔记快捷键
  • 原文地址:https://www.cnblogs.com/lzq1126/p/5596836.html
Copyright © 2020-2023  润新知