目录
一、异或操作介绍
1、异或操作的运算规则
相同为0,不同为1。
2、异或操作的基本运算原则
- 0与任何数进行异或,结果都是任何数。
- 任何数与任何数异或,结果为0。
- 异或运算满足交换律和结合律
3、异或操作的应用
(1)用于两数交换。
注意:用于异或操作的两个数,必须指向不同的地址空间。如果指向同一块地址,则异或操作的结果为0。
(2)用于查找数组中只出现一次的数或缺失的数。
二、编程题示例
1、Leetcode 136:只出现一次的数字
题目描述:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
思路:
根据异或运算的规则,对两个相同数字做异或运算,其结果为0。根据题目描述,数组中的元素只有一个数字出现了一次,其余数字都出现了两次。这样的话通过异或操作正好可以排除掉成对的数字,而最终所得结果即为出现一次的数字。
代码:
class Solution {
public int singleNumber(int[] nums) {
int result = 0;
for(int i=0; i < nums.length; i++){
result ^= nums[i];
}
return result;
}
}
2、Leetcode 137:只出现一次的数字Ⅱ
题目描述:
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
示例 1:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,99]
输出:99
思路:
对本题进行分析,可以发现与上一题很相似,但是通过单一的异或操作无法成功找到出现一次的数字。就数组而言,仅有一个元素出现一次,其余数字出现了三次。
对于数组中的任一元素,其十进制都对应一个32位的二进制数。数组中的元素除取出现一次的数字外,都是三个为一组的。因此可以将数组元素对应的二进制数的对应位进行求和,然后将每一个对应位的和进行模3运算,得到的余数即为出现一次的数组元素所对应的值。
代码:
class Solution {
public int singleNumber(int[] nums) {
int result = 0;
for(int i=0; i < 32; i++){
//对每一位对应的二进制进行求和
int sum = 0;
//移位,判断数组中的每一位
int t = 1 << i;
for(int j=0; j< nums.length; j++){
if((nums[j] & t) != 0){
sum++;
}
}
//如果不能被3整除,则说明一个数的那个数的第i位不是0
if(sum % 3 != 0){
result |= t;
}
}
return result;
}
}
3、Leetcode 260:只出现一次的数字Ⅲ
题目描述:
给定一个整数数组 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]
思路:
1、定义一个变量,遍历数组并进行异或操作。
2、遍历结束即为这两个只出现一次的数字异或的结果。
3、提取出最右边的1,再次遍历数组。即可找到其中一个数。
4、再次异或即可得到两个出现一次的数字。
代码:
class Solution {
public int[] singleNumber(int[] nums) {
int result = 0;
// 异或运算,找到两个出现一次的数字的异或结果
for(int i=0; i < nums.length; i++){
result ^= nums[i];
}
// 找到最右边的1
int rightOne = result & (~result + 1);
// 其中一个出现一次的数字
int singleNum1 = 0;
for(int i=0; i < nums.length; i++){
if((nums[i] & rightOne) == 0){
singleNum1 ^= nums[i];
}
}
// 得到两个出现一次的数字
singleNum1 = result ^ singleNum1;
int singleNum2 = result ^ singleNum1;
// 返回结果
int[] ret = {singleNum1, singleNum2};
return ret;
}
}
4、Leetccode 268:丢失的数字
题目描述:
给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。
示例 1:
输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 2:
输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 3:
输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。
示例 4:
输入:nums = [0]
输出:1
解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。
思路:
和前面几道题很类似,还是使用异或运算。
代码:
class Solution {
public int missingNumber(int[] nums) {
int result = 0;
int n = nums.length;
for(int i=0; i <= n; i++){
if(i == n){
result ^= i;
}else{
result ^= nums[i] ^ i;
}
}
return result;
}
}