在一个数组中找出只出现一次的一个数字、两个数字,而其余数字全部成对出现。

经典题目:

1.在一个数组中找出只出现一次的一个数字,其余数字全部成对出现。

2.在一个数组中找出只出现一次的两个数字,其余数字全部成对出现。

那么,接下来,我们分别对这两个问题进行探讨以及代码实现。

问题1:

       若一个数组中一个数字只出现了一次,其余数字全部成对出现,那么要找出这个出现了一次的数字,办法很简单,对这个数组中的数字进行异或运算就好了,根据异或的性质“相同为0,相异为1”,即可找出该数字。

       So,具体算法我们已经知道了,那么,如何用代码实现呢?


代码如下:

#include <stdio.h>
#include <stdlib.h>
int find_num(int arr[], int sz)
{
	int i = 0;
	int ret = 0;
	for (i = 0; i<sz; i++)
	{
		ret ^= arr[i];
	}
	return ret;
}

void test1()
{
	int arr[] = { 1, 3, 5, 1, 3, 5, 4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int ret = find_num(arr, sz);
	printf("%d\n", ret);
}

int main()
{
	test1();
	return 0;
}
运行结果:


      这样,代码就实现了。


问题2:

        若一个数组中只有两个数字是出现一次,其他数字全部都出现了两次,那么,要找出这两个出现了一次的数组,应该用什么办法呢?
        思考一下......

        首先,从头到尾依次异或数组中的每一个数字,最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了,由于这两个数字肯定不一样,那么异或的结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。
        我们在结果数字中找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0。
        最终,我们对这两个子数组中的元素分别进行异或运算,就可以得到该数组中只出现一次的两个数了。

        So,具体算法我们已经知道了,那么,我们又该如何用代码实现呢?

代码如下:
#include <stdio.h>
#include <stdlib.h>

int find_num(int arr[],int sz)
{
	int i = 0;
	int ret = 0;
	int num1 = 0;
	int num2 = 0;
	int flag = 0; //设置标志位,即1先出现的位置
	for (i = 0; i < sz; i++) //首先,异或数组中的所有元素
	{
		ret ^= arr[i];
	}
	for (i = 0; i < 32; i++) //然后,找异或后该数字二进制最先出现1的位置
	{
		if (((ret >> i) & 1) != 1) //找异或后该数字二进制最先出现1的位置
		{
			flag++;
		}
		else
			break;
	}
	for (i = 0; i < sz; i++) //最后,将数组分成两个子数组,分别异或得到出现一次的数
	{
		if (((arr[i] >> flag) & 1) == 1)
		{
			num1 ^= arr[i];
		}
		else
			num2 ^= arr[i];
	}
	printf("%d %d\n", num1, num2);
}

void test2()
{
	int arr[] = { 1, 1, 2, 2, 3, 4, 5, 5 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int ret = find_num(arr,sz);
}

int main()
{
	test2();
	return 0;
}
运行结果:

       这样,找这两个出现一次的数字就实现了。 


总结一下:

       其实,这两道经典的题目的本质就是掌握异或运算的实质,在此基础上,将问题应用化、简单化,最终对问题进行代码的实现。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值