1.什么是异或
异或(XOR)操作是一种二进制位运算,它对两个二进制数的每一位执行逻辑异或操作。异或操作的符号通常是 ^
(在大多数编程语言中,如C、C++、Java、Python等)。
异或操作的特点是:
- 如果两个比较的位不同,则结果为1
- 如果两个比较的位相同,则结果为0
换句话说,对于任意两个位 A 和 B,A XOR B 的结果可以用以下真值表来表示:
A | B | A XOR B |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
异或操作有几个重要的性质:
- 交换律:A XOR B = B XOR A
- 结合律:(A XOR B) XOR C = A XOR (B XOR C)
- 任何数和0异或都等于它本身:A XOR 0 = A
- 任何数和它自身异或都等于0:A XOR A = 0
- 异或操作可以用来反转特定位:如果你想要反转一个数的第n位(从0开始计数),你可以将这个数与 2^n 进行异或操作。
异或操作在多种场景下都很有用,包括但不限于:
- 无进位加法:两个数进行异或操作的结果相当于这两个数不进位相加的结果。
- 加密/解密:在一些简单的加密场景中,异或操作可以用来加密或解密数据,因为两次相同的异或操作可以相互抵消。
- 错误检测:在数据传输或存储过程中,可以使用异或校验来检测数据是否发生变化。通过将所有数据块进行异或操作并存储结果,然后在接收或读取时再次对所有数据块进行异或操作,如果结果与存储的校验值不同,则说明数据在传输或存储过程中发生了错误。
- 寻找数组中唯一的重复或缺失元素:在某些特定情况下,可以使用异或操作来找出数组中唯一重复或缺失的元素。
2.为什么OJ练习使用异或后,所缺失的数就是x
oj链接:面试题 17.04. 消失的数字 - 力扣(LeetCode)
异或操作后变量里存储的那个数之所以是缺失的数,主要依赖于异或操作的几个关键性质,特别是其交换律、结合律以及任何数和它自身异或都等于0的性质。
在给定的问题中,我们有一个从0到N的连续整数序列,但其中一个数缺失了,形成了一个长度为N+1的数组(假设数组是从0开始索引的,并且包含了从0到N-1的整数,除了一个缺失的数)。我们的目标是找出这个缺失的数。
我们可以按照以下步骤使用异或操作来找到这个缺失的数:
- 初始化一个异或变量:首先,我们初始化一个变量(比如叫
xorResult
)为0。这个变量将用于存储最终的异或结果。 - 对所有应该出现的数进行异或:接下来,我们遍历从0到N-1的所有整数(因为序列是从0开始的,且缺失了一个数,所以实际数组长度是N+1,但我们只遍历到N-1来模拟完整的序列),并将它们与
xorResult
进行异或操作。这一步的结果是将所有应该出现在数组中的数(包括那个缺失的数)都进行了异或。 - 对数组中的实际数进行异或:然后,我们遍历数组中的每个元素,并将它们也与
xorResult
进行异或操作。由于数组中包含了从0到N-1的整数中的N个(因为缺失了一个),这一步实际上是将除了缺失的那个数之外的所有数都与xorResult
进行了两次异或操作(一次是在第2步中,另一次是在这一步中)。 - 利用异或的性质找出缺失的数:由于任何数和它自身异或都等于0,所以在第3步中,除了缺失的那个数之外的所有数都与
xorResult
进行了两次异或操作,相互抵消了。而缺失的那个数只在第2步中与xorResult
进行了一次异或操作,因此在xorResult
中保留了下来。 - 最终,
xorResult
中存储的就是那个缺失的数。
这个过程之所以有效,是因为异或操作满足交换律和结合律,允许我们将多个数的异或操作合并成两步:先对所有应该出现的数进行异或,再对实际出现的数进行异或。而任何数和它自身异或的结果为0的性质,则确保了除了缺失的那个数之外的所有数都相互抵消了。
3.异或的原理
异或(XOR)操作的原理主要基于二进制数的位运算规则。在二进制系统中,每个数字都由一系列的0和1组成,这些0和1分别代表不同的位(bit)的值。异或操作是一种对两个二进制数的每一位进行逻辑运算的操作,其结果也是一个二进制数。
异或操作的原理可以简单概括为以下几点:
- 逐位比较:异或操作会对两个二进制数的每一位进行逐位比较。
- 逻辑判断:对于每一对相应的位(即两个数的同一位),如果它们相同(都是0或都是1),则异或操作的结果是该位为0;如果它们不同(一个是0,另一个是1),则异或操作的结果是该位为1。
- 结果组合:将所有位的异或结果组合起来,形成最终的异或结果。
这个原理可以用真值表来表示:
A(输入1) | B(输入2) | A XOR B(输出) |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
异或操作有几个重要的性质,这些性质也是其原理的体现:
- 交换律:A XOR B = B XOR A。这意味着异或操作是可交换的,两个数的异或顺序不影响结果。
- 结合律:(A XOR B) XOR C = A XOR (B XOR C)。这意味着多个数的异或操作可以分组进行,而不影响最终结果。
- 零元素:任何数与0进行异或操作都等于它本身。即 A XOR 0 = A。
- 自反性:任何数与它自身进行异或操作都等于0。即 A XOR A = 0。这个性质在解决一些特定问题时非常有用,比如找出一个数组中唯一的重复元素或缺失的元素。
4.异或的结果
- 那么,为什么xorResult中存储的就是以获得最终结果呢?
在二进制系统中,0 和 1 是最基本的表示单位,它们分别代表“关”和“开”或“假”和“真”的状态。然而,在异或(XOR)操作的上下文中,0 或 1 并不是异或操作结果的唯一可能值;实际上,异或操作的结果可以是任何二进制数,具体取决于输入的二进制数。
当我们说异或操作的结果是 0 或 1 时,我们实际上是在谈论异或操作对每一位的影响。异或操作的规则是:如果两个比较的位相同,则结果为 0;如果两个比较的位不同,则结果为 1。这意味着对于每一位,异或操作都会根据这一规则产生一个 0 或 1 的结果。
然而,当我们对整个二进制数进行异或操作时(即不是单独对某一位,而是对整个数字的所有位),结果将是一个新的二进制数,其每一位都是根据异或规则计算得出的。这个新的二进制数可以表示为一个十进制数(如果我们选择这样做的话),但它仍然是由 0 和 1 组成的二进制序列。
在上述oj题目中,xorResult 变量中存储的是所有应该出现的数与数组中实际出现的数进行异或操作后的结果。这个结果是一个二进制数,其每一位都是根据异或规则计算得出的。根据异或的自反性,由于缺失的那个数只被异或了一次(而其他所有数都被异或了偶数次,从而相互抵消),xorResult 最终将包含缺失的那个数的二进制表示。
因此,0 或 1 在这里不是异或操作结果的唯一体现;相反,它们是构成异或操作结果(即xorResult)的二进制序列中的基本元素。整个xorResult是一个由 0 和 1 组成的二进制数,它代表了缺失的那个数的二进制表示。如果我们选择将xorResult转换为十进制数以便更容易地理解或显示,那么我们可以这样做,但这不会改变它作为二进制数的本质。
5.OJ代码
int missingNumber(int* nums, int numsSize)
{
int i=0;
int xorResult=0;
for(i=1;i<=numsSize;i++)
{
xorResult^=i;
}
for(i=0;i<numsSize;i++)
{
xorResult^=nums[i];
}
return xorResult;
}