LeetCode 260.只出现一次的数字III

题目描述

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。

示例 2:
输入:nums = [-1,0]
输出:[-1,0]

示例 3:
输入:nums = [0,1]
输出:[1,0]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number-iii

思路分析

对于此类问题,我们首先应该想到的就是一个非常实用的操作符‘^’,异或操作!

对于异或操作在这里做出以下基本介绍:

    int a = 10;
	int b = 10;
	int c = a^b;//0

① 对于a,b两个变量,初始值一致时,对他们做异或运算结果为零!
② 异或运算满足交换律,结合律。
即 a^b = b^a
a^b ^c = (a^b) ^c = a^ (b^c)
③ 0异或任意数结果为任意数

简化模型,分析实质

现在我们思考一个比较简单的问题:

如果说对于一个整形数组nums,其中只有1个数据出现一次,其余均出现两次,也就是说,我们对数组中所有数据进行异或操作,最终的结果就是两、这个只出现一次的数据。(原因:根据上面介绍的三条异或基本性质可以得出:相同数据异或均为0,最后结果就是0与只出现一次的数据进行异或,结果就是我们要找的数据!!)

下面画个简图作为详细说明:
在这里插入图片描述
上图阐述了循环具体过程以及最终结果。

回归问题,拓展应用

反过来看看数组中有两个不同元素能不能运用同样的方法??
首先分析一下:我们对数组所有元素做异或运算,结果是什么呢??很显然,就是数组中两个只出现一次的数据异或的结果!
也就是说我们这样得到的数据是两个只出现一次数的异或,而并非两个数。这与我们期望的结果不一致。那该怎么办呢??

我们回到异或运算的本质:异或运算是对两个数据的逐比特位进行异或,相同为0,不同为1

那么我们对于两个不同数异或后的结果进行分析:
例如:
8的二进制表达为0000 1000(以8个比特位作为例子说明)
12的二进制表达为:0000 1100
8^12就等同于0000 1000 ^0000 1100
结果为:0000 0100
观察标黄的三个二进制序列,我们不难发现,异或结果为1的位置,对应在两个数据处比特位恰好相反。

也就是说,我们对于数组中所有元素做异或操作之后得到的数据的二进制序列中值为1的位置就是对应到做异或操作的两个数相应位置的比特位值相异

PS:这里是解决该题的一大关键点,务必理解到位后往下看!

到此,我们就有更进一步的解题思路了。既然这样,我们就可以按照上述特点以异或结果中任意一个比特位为1的位置作为参照标准,对整个数组进行划分,这样就将求两个出现一次的数据问题变成较为简单的球一个数的问题,按照前面引入例子的方法进行操作就好了!

有同学就又有疑问啦。在划分过程中原数组中出现两次的数据会不会被划分到不同的组中去做异或操作呢??==当然不会!!==相同数据的对应比特位一定相同,那么按照我们规定的标准划分时他们肯定会在同一组中。

看到这里,我们的分析过程已经基本搞定!!接下来废话不说,直接上代码:

代码展示

#include <stdio.h>
#include <windows.h>
/*
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。
找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
*/

void FindTwoDiffers(int *arr,int len,int *retArr)
{

	int sum = 0;
	for (int i = 0; i < len; i++)
	{
		sum ^= arr[i];
	}
	int j = 0;
	while (j<32)
	{
		if (sum&(1 << j))
			break;
		else
			j++;
	}
	int num1 = 0;
	int num2 = 0;
	for (int k = 0; k < len; k++)
	{
		if ((arr[k] & (1 << j))==0)//注意运算符优先级问题
		{
			num1 ^= arr[k];
		}
		else
		{
			num2 ^= arr[k];	
		}
	}
	retArr[0] = num1;
	retArr[1] = num2;
}

int main()
{
	int arr[] = { 3, -3, 5, 7, 8, 2, 10, 3, 5, 7, 8, 2 };
	int len = sizeof(arr) / sizeof(arr[0]);
	int retArr[2] = {0};
    FindTwoDiffers(arr, len,retArr);

	for (int i = 0; i < 2; i++)
	{
		printf("%d ",retArr[i]);
	}
	system("pause");
	return 0;
}

在这里插入图片描述
以上就是对这道题目的详细解答,路过的各位看官留下你们足迹~~

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Suk-god

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值