一、背景
最近刷到一道算法题:找到数组中只呈现一次的数字。
题目形容是这样的:
给定一个非空整数数组,除了某个元素只呈现一次以外,其余每个元素均呈现两次。找出那个只呈现了一次的元素
阐明:你的算法应该具备线性工夫复杂度。你能够不应用额定空间来实现吗?
示例:
输出:[4, 1, 2, 2, 1]
输入:4
在不看阐明的状况下,大聪慧脑海里立即就想到了利用对象来存储数组中已呈现数字,再呈现则delete
该数字,最初对象中只剩下惟一数字。这个也是暴力解题的思路之一。
Talk is cheap,Show me your code:
/** * @param {number[]} nums * @return {number} */const singleNumber = function(nums) { const result = {}; for (let i = 0; i < nums.length; i++) { if (result[nums[i]] === undefined) { result[nums[i]] = nums[i]; } else { delete result[nums[i]]; } } return Object.values(result)[0];};
问题是解决了,但性能却个别:
再细想一下,不应用额定空间的状况下,最快捷的形式就是先将数组排序,再比拟相邻数字是否相等。是不是一伶俐,连忙上代码:
/** * @param {number[]} nums * @return {number} */const singleNumber = function(nums) { const newNums = nums.sort(); let num = newNums[0]; if (newNums.length === 1) { return num; } for(let i = 0; i < newNums.length; i++) { if (newNums[i] !== newNums[i + 1]) { if (i === 0) { num = newNums[0]; break; } // 可前置判断是否数组越界 // if (i + 3 > newNums.length) { // num = newNums[i + 1]; // break; // } if (newNums[i + 1] !== newNums[i + 2]) { num = newNums[i + 1]; break; } } } return num;};
不出所料,问题解决了,性能也有所晋升:
是不是忽然感觉,本人又行了!!
正当我认为这样就能够交作业时,我看了下其他同学的解题思路,我才发现,我真的是个大聪慧。有种数学运算符秒秒钟解决:异或运算
先上代码:
/** * @param {number[]} nums * @return {number} */const singleNumber = function(nums) { return nums.reduce((a, b) => a ^ b, 0);};
你没看错,就是一行代码!!!!更惊艳的还在后头,来看看性能如何:
果然,匮乏的常识,限度了我的想象力。当然能够还有更牛逼的解题办法,也欢送大家在评论区交换,那接下来就跟大家介绍下异或运算。
二、简介
大家比拟相熟的逻辑运算,次要是“与运算”(AND
)和“或运算”(OR
),还有一种“异或运算”(XOR
)也十分重要。
XOR
,是exclusive OR
的缩写,次要用来判断两个值是否相等。
XOR
个别应用插入符号(caret
)^
示意。如果约定0 为 false
,1 为 true
,那么 XOR
的运算真值表如下:
0 ^ 0 = 0;0 ^ 1 = 1;1 ^ 0 = 1;1 ^ 1 = 0;
2.1 运算定律
一个值与本身运算,总为
false
x ^ x = 0;
一个值与
0
运算,总为其自身x ^ 0 = x;
可替换性
x ^ y = y ^ x;
联合性
x ^ (y ^ z) = (x ^ y) ^ z;
三、利用
依据下面的这些运算定律,能够失去异或运算的很多重要利用。
3.1 简化运算
多个值的异或运算,能够依据运算定律进行简化:
a ^ b ^ c ^ a ^ b= a ^ a ^ b ^ b ^ c= 0 ^ 0 ^ c= c
3.2 交互值
两个变量间断进行三次异或运算,能够互相交换值。
let a = 1;let b = 2;a = a ^ b;b = a ^ b;a = a ^ b;console.log(`a: ${a}, b: ${b}`); // a: 2, b: 1
这是两个变量替换值的最快办法,不须要任何额定的空间。也是一开始找出数组中只呈现一次的数字的解题要害。
3.3 加密
异或运算能够用于加密。
第一步,明文:text
和 密钥:key
进行异或运算,能够失去密文:cipherText
text ^ key = cipherText;
第二步,密文与密钥再次进行异或运算,就能够还原成明文
cipherText ^ key = text
原理很简略,如果明文是 x
,密钥是 y
,那么 x
间断与 y
进行两次异或运算,失去本身。
(x ^ y) ^ y= x ^ (y ^ y)= x ^ 0= x
3.4 数据备份
异或运算能够用于数据备份。
文件 x
和文件 y
进行异或运算,产生一个备份文件。
x ^ y = z
当前,无论是文件 x 或文件 y 损坏,只有不是两个原始文件同时损坏,就能依据另一个文件和备份文件,进行还原。
x ^ z= x ^ (x ^ y) = (x ^ x) ^ y= 0 ^ y= y
下面的例子是 y
损坏,x
和 z
进行异或运算,就能失去 y
。
四、结束语
工欲善其事,必先利其器。不断完善本身的常识体系,能力发现不一样的世界,respect!!
五、参考链接
That XOR Trick