话说在那狗星上,生活着一群情侣狗和单身狗,但单身狗的比例太高,让狗星管理员紫薯精发了愁,聪明的狗管理紫薯精 灵光一闪 “我只要消灭所有的单身狗,单身比例不就能下来了吗^ _ ^”,于是他带上手套,打了个响指,可是,怎么能在狗群里找到单身狗呢?
任给一个2N+1个无序元素,其中N对为成双数字,一个为成单数字X,求出x的值;
例如:arr[9]= { 2, 4, 3 ,5 ,5, 4, 2, 1, 3 } 则成单数字为1。
不难想到,可以通过不断遍历来寻找相同的数字,以上为例:首先保存z=arr[0];不断的向后遍历是否找到arr[y]=x,若无则z=arr[1],继续重复,直到找到X。
代码示例:
#include<stdio.h>
int main()
{
int arr[9] = { 2, 4, 3 ,5 ,5, 4, 2, 1, 3 };
for (int i = 0; i < 9; i++)
{
int flag = 1; //用flag来记录是否出现重复数字,出现后flag置0
int x = *(arr + i);
for (int j = 0; j < 9; j++)
{
if (i == j) //当遍历到其本身时,用continue来跳过其本身
continue;
else if (x == *(arr + j))
flag = 0;
}
if (flag)
printf("%d\n", x);
}
}
但不难发现,其时间复杂度O(n^2),狗管理对这杀狗效率非常不满,为了更高效的猎杀单身狗,我们今天将要引入一个操作符 ^ (按位异或)
^ c语言相较于其他语言,其有一特点是允许执行位操作,众所周知,计算机中任何数据都是转换为二进制储存。以int类型的3和5为例,转换为二进制后,异或操作为 比较相同每一位,均为1或0则得1,不同则为1;
转换为十进制为 3^5 = 6;
由此我们易推出
1. 任何数异或其本身都为0
2 0 异或任何数X都为X
3. (a^b)^c==a^(b^c)
基于以上考虑,可得任何对成双数字异或后得到均为0;且0异或一个成单数字得其本身;
so 这为我们更好的找到单身狗提供了思路:只需将所有数字全部异或便得到成单数字;
#include<stdio.h>
int main()
{
int arr[9] = { 2, 4, 3 ,5 ,5, 4, 2, 1, 3 };
int x = 0;
for (int i = 0; i < 9; i++)
{
x = x ^ *(arr + i);
}
printf("%d", x);
}
这时代码更加简洁,且时间复杂度为O(n),紫薯精开心的笑了;
ONE MORE
当再加入一只单身狗时,还能将他们找出吗?
例;arr[10]= { 2, 4, 3 ,5 ,5, 4, 2, 1, 3 , 8} (现有成单数字8,1)
由上可知,如果我们还是将他们全部异或时,得到的X=8^1
不妨设成单数字为 D S ,,则我们可以得到一个X=D^S;
但仅仅是一个X,我们是可以将其分为许多组解,而其中仅有D S是满足题意的
所以,我们就会想,是否可以将D S分成两组进行异或,由于D!=S,则X=D^S必有某位为1,则D 和 S 在该位上必有一个是0,一个是1,所以我们可以依照是否某位上为1或0将数组分为两组分别求异或;
还是以 arr[10]= { 2, 4, 3 ,5 ,5, 4, 2, 1, 3 , 8}为例;1^8其中第1和第4位为1,则我们可以将他们按照第1位是否为1(这时候就相当于分奇偶)
代码如下:
更更更进一步:
当有n只单身狗时,是否能找出他们呢?
思考: 仿照上面的思想,第一遍全体异或时,我们可以得到
X=n_1 ^ n_2 ^ n_3......n_n-1 ^ n_n; 的数,再以某一位是否为1将全体分为两组,再两组分别异或,
每组再分成两组,重复操作直到每组仅有一个成单数字时结束.
但,反思后发现,由于我们并不知道具体的数字是什么,每次分组时无论是成双数字还是成单数字,
可以说是随机分配的,没有一个指标能够显示出组内仅有一个成单数字,
比如 共有4只单身狗,第一次分组时,他有可能是分成2+2,也有可能是1+3 (且每组的数字个数也是随机的,不一定相等),所以目前还未想出依据什么来判断是否仅有一只单身狗----未完待续??
✿ヽ(°▽°)ノ✿
关于异或的其他操作
1 .异或操作可以让我们不借助中间变量来实现两数交换
例如 a=1,b=2, 交换后 a=2,b=1;
{ a=a^b;
b=a^b;
a=a^b;
}
(其实加法也可以实现 a=a+b; b=a-b; a=a-b;)
找两只单身狗的代码段
#include<stdio.h>
int main()
{
int arr[10] = { 2, 4, 3 ,5 ,5, 4, 2, 1, 3 ,8};
int x = 0;
for (int i = 0; i < 10; i++)
{
x = x ^ *(arr + i);
}
int y = 1;
while (x % 2 == 0) //找到第几位是1,用y记录。
{
y++;
x = x >> 1;
}
int D = 0;
int S = 0;
for (int i = 0; i < 10; i++)
{
if ((*(arr + i) >> (y - 1)) % 2 == 1) //以第几位是否为1来将所有元素分为两组;
{
D = D ^ *(arr + i);
}
else
{
S = S ^ *(arr + i);
}
}
printf("%d %d", D, S);
}