第二题不是位运算,可以先看。
剩下三道从简单到难:4=>3=>1
只出现一次的数字系列(均可位运算):
1.相同的出现三次,多余一个不相同的
剑指 Offer 56 - II | 336 | 79.2% | 中等 |
137 | 286 | 68.2% | 中等 |
两题一样
法一:sort+比较相邻元素
var singleNumber = function(nums) {
nums.sort();
if(nums[0] !== nums[1]) return nums[0];
if(nums[nums.length-1] !== nums[nums.length-2]) return nums[nums.length-1];
for(let i = 1; i < nums.length - 1; i++){
if(nums[i] !== nums[i-1] && nums[i] !== nums[i+1]) return nums[i];
}
// return null;
};
感觉没有把题给条件(出现三次)全部用上,此法有点太丑
补充:for循环几种方式:
① 普通for(let i=0;i<arr.length;i++)
②for in : for(let x in arr) 其中x也是索引,若arr换为obj,则x为键
③for of(ES6):for(let num of nums) 其中num是元素
法二:创建对象+ES6 for of
var singleNumber = function(nums) {
// 创建对象。for of
let obj = {};
for(let x of nums) {
obj[x]? obj[x]++ :obj[x] = 1;
}
for(let x in obj) {
if(obj[x] == 1) return x;
}
};
但有更好的方法:位运算。
法三:
var singleNumber = function(nums) {
let res = 0;
for (let bit = 0; bit < 32; bit++){
let count = 0;
let mask = 1<<bit;
for(let n of nums) {
if((mask & n) !== 0) count++;
}
if(count % 3) res = res | mask;
}
return res;
};
用到了位与,位或,移位
声明一个mask,mask的1从最后一位逐渐往前移动,和数组内的各元素的二进制的某一位比较,计数count,内层循环结束后将mask和res取或。
“如果一个数字出现3次,它的二进制每一位也出现的3次。如果把所有的出现三次的数字的二进制表示的每一位都分别加起来,那么每一位都能被3整除。 我们把数组中所有的数字的二进制表示的每一位都加起来。如果某一位能被3整除,那么这一位对只出现一次的那个数的这一肯定为0。如果某一位不能被3整除,那么只出现一次的那个数字的该位置一定为1。”
2.= =
剑指 Offer 55 - I | 662 | 78.7% | 简单 |
这几道位运算的题中我穿插了一道递归二叉树深度...
直接递归做:不断找左子树和右子树中的最大值,直接return,最后加1
var maxDepth = function(root) {
if(!root) return 0; //终止条件
return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
};
3.相同的出现两次,多出的两个各不相同
剑指 Offer 56 - I | 609 | 70.9% | 中等 |
260 | 243 | 74.0% | 中等 |
两题一样
如果没有空间复杂度O(1)的条件,可以用for来做
var singleNumbers = function(nums) {
let obj = {};
let arr = [];
for(let num of nums) {
obj[num]? obj[num]++ : obj[num] = 1;
}
for(let num in obj) {
if(obj[num] == 1) arr.push(num);
}
return arr;
};
但不符题意,而且好像很蠢...
用位运算:异或
①异或的性质
两个数字异或的结果a^b是将 a 和 b 的二进制每一位进行运算,得出的数字。 运算的逻辑是
如果同一位的数字相同则为 0,不同则为 1
②异或的规律
任何数和本身 异或 == 0
任何数和 0 异或 == 本身,可用n & 1 == 1或0来判断n的奇偶性,即最后一位是否是1
③异或满足交换律。 即 a ^ b ^ c ,等价于 a ^ c ^ b
var singleNumbers = function(nums) {
let a = 0;
let index = 0;
for(let num of nums) {
a ^= num;
}
while((a & 1) ==0){
index++;
a >>= 1;
}
let r1 = 0;
let r2 = 0;
for(let num of nums) {
// 以4,4,1,6为例,分为441和6这两组
if (((num >> index) & 1) == 0){
r1 ^= num;
} else {
r2 ^= num;
}
}
return [r1,r2];
};
4.相同的出现一次,只多出一个不同的数
136 | 1248 | 70.3% | 简单 |
此题比上题简单,因为只需提取1个数据,直接异或然后返回结果即可
var singleNumber = function(nums) {
let a = 0;
for (let n of nums) {
a ^= n;
}
return a;
};