探究异或——从数组找出只出现一次的数据

  • Post author:
  • Post category:其他

题目描述
在每日一练中有一道简单的题目:从数组中找出只出现一次的数据,具体描述如下:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
要求:线性时间复杂度,不使用额外空间。
例如给定数组[2,2,1],输出应为1;数组[4,1,2,1,2]结果为4。
思路探究
首先想到的思路为数组每个数循环与其他数字比较,找到不相等的数:

#include <stdio.h>

int nums[] = {1,1,2,3,4,5,4,5,2};    //给定一个测试数组 

int main(){
	int ret,i,count,arraySize,flag;
	
	arraySize = sizeof(nums) / sizeof(int);    //计算数组大小 
	for(count = 0;count < arraySize;count++)     //循环数组中的每一个数 
	{
		flag = 0;    //是否有其他相同数标志 
		for(i = 0;i < arraySize;i++)
		{
			if(i != count && nums[i] == nums[count])    //与数组中其他数比较 
			{
				flag = 1;    //有其他相同的数 
				break;
			}
		}
		
		if(flag == 0)    //找到单独的数,输出,结束程序 
		{
			printf("%d",nums[count]);
			break; 
		}
	}
	
	return 0;
}

运行结果如下:
在这里插入图片描述

但这种笨方法并不符合线性时间复杂度的要求,正确的方法是利用异或运算的特性解题:
1、一个数和0异或保持不变;
2、一个数和自身异或结果为0。
那么按题目要求的偶数个数组成员异或后会为0,唯一的一个奇数个成员与0异或则为想要的结果:

#include <stdio.h>

int nums[] = {1,1,2,3,4,5,4,5,2};    //给定一个测试数组 

int main(){
	int ret,i,arraySize;
	
	ret = 0;
	arraySize = sizeof(nums) / sizeof(int);    //计算数组大小 

	if(arraySize == 2 || arraySize == 0) return;    //数组成员两个不符合题意 
	for(i = 0;i < arraySize;i++) ret ^= nums[i];    //依次异或 
	printf("异或方法:%d",ret);  
	
	return 0;
}

运行结果如下:
在这里插入图片描述
异或探究
进一步思考的话,凭借异或的两个特性,如果数组是这样[1,1,2,2,3,3,5]的话很好理解:0 ^ 1 = 1,1 ^ 1 = 0,0 ^ 2 = 2,2 ^ 2 = 0……。而程序中为什么可以忽略数组变量的排列顺序得到想要的结果呢?
通过两个例子来看:
[1,3,1,3]异或:
在这里插入图片描述
[1,2,3,4]异或:
在这里插入图片描述
可以看出,因为异或是按位运算,多个数异或其实就是根据这一位上1的奇偶判断:
1、偶数个1(包括0),该位为0;
2、奇数个1,该位为1。
由此,带入上面的题目就很好理解了,除开单独的数,其他数异或运算时每一位上1的个数一定是偶数个。
简单的可以理解为异或的运算顺序与“+”类似,可以随意的调换运算顺序而不影响结果。
同样的,使用异或交换值的过程就很好理解了:

a = a ^ b;
b = a ^ b;
a = a ^ b;

第二步b = a ^ b转换为b = a ^ b ^ b = a;
第三步a = a ^ b转换为a = a ^ b ^ a = b;


版权声明:本文为m0_46222499原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。