题目:
给定一个整数数组arr,其中只有两个数出现了奇数次,其他的数都出现了偶数次,找出这两个数
例如:
arr=[1,5,1,5,2,6,4,6];
结果2,4
要求:
时间复杂度O(n),额外空间复杂度O(1)
解:
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("input arr size: ");
int arr_size;
scanf("%d", &arr_size);
int *arr = (int *)malloc(sizeof(int)*arr_size);
for (int i = 0; i < arr_size; i++)
scanf("%d", &arr[i]);
//求出这两个数异或的结果。
int rtwo = 0;
for (int i = 0; i < arr_size; i++)
rtwo ^= arr[i];
// printf("%d\n", rtwo);
//求这个结果中为1的位,这个位两个数必定一个为1,一个为0,因为只有这样,这一位的结果才为1。
int tbit = 1;
while (1)
{
if (tbit & rtwo)
break;
tbit<<=tbit;
}
//把数组分为tbit位为0的和tbit为1的两组。必然一个结果在一组,另一个结果在另一组。
//每组里除了相同的数也都被分到了一组。所以每组里除了结果其余的数仍出现偶数次
int r1 = 0, r2 = 0;
for (int i = 0; i < arr_size; i++)
if (tbit&arr[i])
r1 ^= arr[i];
else
r2 ^= arr[i];
printf("%d %d\n", r1, r2);
}
原理:
k与0异或结果为k,k与k异或结果为0.
假如数组中只有一个数出现奇数次,其余的数都出现偶数次,数组从头到尾异或后,出现偶数次的数都被"消去了",异或的结果就是这个出现奇数次的数。
如果有两个数出现了奇数次,数组从头到尾异或后的结果就是这两个数异或的结果。
怎么"分开"这两个数呢?找到异或结果为1的一位(异或结果为1,说明这两个数这一位必定一个为1一个为0),用这一位把数组分成两组,每组分别异或,就能分别得到两个数了。
需要两次循环n趟,时间复杂度O(2n)=O(n)
需要一个整型变量保存两个数异或的结果。一个整型变量保存两数位不相同的位。额外空间复杂度为O(1)