求大佬帮我看一下这段代码

weixin_45906870 2020-09-14 08:17:49
题目:错误的集合
集合 S 包含从1到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个元素复制了成了集合里面的另外一个元素的值,导致集合丢失了一个整数并且有一个元素重复。

给定一个数组 nums 代表了集合 S 发生错误后的结果。你的任务是首先寻找到重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。

示例 1:

输入: nums = [1,2,2,4]
输出: [2,3]
注意:

给定数组的长度范围是 [2, 10000]。
给定的数组是无序的。

代码如下:
int* findErrorNums(int* nums, int numsSize, int* returnSize){
int resLianxu = 0;
for(int i = 1; i < numsSize + 1; i++)
{
resLianxu ^= i; //从这里看出来我知道这题是用异或运算解的
}
int resUnLianxu = 0;
for(int j = 0; j < numsSize; j++)
{
resUnLianxu ^= nums[j];
}

int resOr = resUnLianxu ^ resLianxu;
int lastbit = resOr & (-resOr); //从这一步开始我就看不懂了,这里为什么要用到按位与运算?为什么要用相反数?请大佬从这里开始往下解释
int res1 = 0;
int res2 = 0;
for (int m = 0; m < numsSize; m++)
{
if((nums[m] & lastbit) == 0) res1 ^=nums[m];
else res2 ^=nums[m];
}
for (int n = 1; n < numsSize+1; n++)
{
if((n & lastbit) == 0) res1 ^=n;
else res2 ^=n;
}
int *res = (int*)malloc(2 * sizeof(int));
res[0] = res1;
res[1] = res2;
for (int z = 0; z < numsSize; z++)
{
if(res2 == nums[z])
{
res[1] = res1;
res[0] = res2;
}
}
*returnSize = 2;
return res;
}
...全文
116 1 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
qybao 2020-09-15
  • 打赏
  • 举报
回复
说实话,这种算法属于技巧,不太适合初学者(没有一定的数学和逻辑运算基础就理解不了)。 首先,你要知道以下逻辑运算 a^b,即 a 异或 b,当a==b时,异或结果是 0,也就是一个数和自己本身(相同的数)做异或运算,结果是0。 a^0==a,即任意数和0做异或,结果是该数本身。 a^b^c==a^c^b==b^a^c==c^a^b==c^b^a,也就是异或本身满足交换律,即不管操作数字在前在后异或结果不变。 所以,先来理解 resOr 的结果是什么?以LZ的样例数据来说明 错误集合 ns = {1,2,2,4},resUnLianxu是错误集合所有元素的异或 正确集合 as = {1,2,3,4},resLianxu是正确集合所有元素的异或 resOr = resUnLianxu ^ resLianxu; 就是 错误集合所有元素的异或结果 和 正确集合所有元素的异或结果 再做异或 相当于 nums 集合 加 arrs 集合的并集的所有元素的做异或运算,即 1^2^2^4^1^2^3^4 上面说了,异或的交换律(重新调整一下异或的元素),以及相同数的异或结果是0,任意数和0做异或结果是任意数本身,可知, resUnLianxu ^ resLianxu = 1^1(相同数)^2^2(相同数)^2^3(不同数)^4^4(相同数) = 0^0^2^3^0=2^3 所以 resOr 实际上就是错误集合和正确集合的差异元素的异或结果 接下来,我们把两个集合(错误集合和正确集合)的元素分组,如果我们刚好能把这两个有差异的元素分在不同的两个组, 按照上面相同的方式,我们再把分好组的元素再来做异或,就可以求出差异的元素,比如 错误集合分组 ns1 = {1}, ns2 = {2,2,4} //差异数2分在第二组 正确集合分组 as1 = {1,3}, as2 = {2,4} //差异数3分在第一组 那么上面我们知道,如果ns1和as1合起来取异或,就能得到差异元素3(1^1^3=0^3=3), ns2和as2合起来取异或,就能得到差异元素2(2^2^4^2^4=2^2^4^4^2=0^0^2=2) 那么怎么能正确分成这样的组呢?很自然我们首先会想到奇偶性,奇数分一组,偶数分一组。 但是如果差异的两个数奇偶性相同,也就是两个数同为奇数或同为偶数(比如差异元素是3和5,或者2和6),还是不能正确分组,所以奇偶性行不通。 那么好了,我们再来看,首先我们知道任意相同的数a,和某个数b做运算,结果肯定相同,即 a?b,只要a相同,a?b的结果肯定相同。 所以两个集合相同的元素我们不用考虑,我们只需要关心差异的两个数,让它们和某个数经过某个操作后结果不同即可正确分组, 也就是,让差异数1和某个数操作后变为0,差异数2和某个数操作变为非0即可。 这时我们又想到了 resOr(因为resOr刚好是两个差异数异或的结果)。 那么怎么通过resOr来区分两个差异的数呢?这就是 resOr & (-resOr)。 从异或的概念可知a^b的结果为a和b的二进制位不相同的部分,把这二进制位不相同的部分抽出来,取反+1(二进制补码,也就是取负数), 再和原来的抽出来的部分做与(&)运算,就会保留1位二进制位和a或b相同的数(因为是取反+1,取反部分&结果为0,+1部分被保留,具体可以自己手动用二进制推导)。 这样,如果该数的保留的二进制位和a相同,那它必然和b不同(因为异或就是取a和b不同的部分),这样与b做与(&)运算结果必为0。 反之,如果该数的保留的二进制位和b相同,那它必然和a不同,这样与a做与(&)运算结果必为0。 至此,我们就可把有差异的两个数正确分组了。能正确分组后,剩下的就是按上面分析的逻辑处理即可。 for循环的if ((nums[i]&lastbit)==0) 就是分组的意思。错误集合分成 if else 两个组,正确集合也分成 if else 两个组, 分成的这两个组刚好把有差异的两个数分在了不同的组,这样,通过上面分析的异或运算,即可找出有差异的两个数。

70,020

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧