/** * 异或运算是一种位运算,相同为0,不同为1,比如110 ^ 001结果为111,也可看成无进位加法这点非常有用 */ /** * 使用疑惑交换两个变量的值 */ function swap() { let a = 1; let b = 2; a = a ^ b; b = a ^ b; a = a ^ b; console.log(a); console.log(b); } /** * 比较两个数,返回其中较大的那个 */ function getMax(a, b) { let flag = (a - b) >>> 31; // 如果b大的话,结果就是个负数,负数的符号位为1 return a * (flag ^ 1) + b * flag; /* 解释:比较两个数的大小最直观的方式就是作差,然后判断差是否大于零,如果大于零则说明 被减数大,如果小雨零则说明减数大,我们只要拿到最高位,如果是1,我们就返回减数b,否则 返回减数a,这个时候我们需要找到某种运算在falg为1的时候它要为0,flag为0的时候它要为1 所以我们把flag^1,并分别把(flag^1)和flag分别作为a,b的乘数,相加的结果就是我们要求 的最大数。这里面之所以没有用到判断是因为,位本身就表示一种状态。 */ } /** * https://leetcode.cn/problems/missing-number/ * 给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。 * @param {number[]} nums - 如果把缺失的数补上,排序后下标和值是一一对应的 */ function missingNumber(nums) { let eorAll = 0, eorHas = 0; for (let i = 0; i < nums.length; i++) { eorAll ^= i; eorHas ^= nums[i]; } eorAll ^= nums.length; return eorAll ^ eorHas; } /* 分析:按照题目要求,如果没有缺少那个数的话,0-n应该是n+1个数,而且和下标一一对应,因为 它们是连续的,只有那个多出的数是没有下表与其对应的(这里的对应表示它们的值相等),根据异或 的运算特点,两个相同的数做异或运算的结果为0,任何数和0做运算为本身,我们只需把所有下标异或的结果 和所有值异或的结果做一次异或运算就可以得到多出来的那个数,如果之间缺少一个数的话数组中最后一个值 对应的是数组下标,所以也要把数组下标异或上。 */ /** * https://leetcode.cn/problems/single-number/ * 数组中1种数出现了奇数次,其他的数都出现了偶数次,返回出现了奇数次的数。 * @param {number[]} nums - 这个数组里面有某个数只出现了奇数次 */ function singleNumber(nums) { let eor = 0; for (let num of nums) { eor ^= num; } return eor; } /** * 原理:这个很简单,两个相同的数异或结果为0,任何数和0异或结果为本身,如果数组中所有 * 的除了一中出现奇数次,其余数属出现偶数次,那么异或的结果必定是这个出现奇数次的数,因为 * 所有偶数次的数异或结果为0,0在和这个出现奇数次的数异或,结果为这个奇数次的数。 */ /** * 给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。 * @param {number[]} nums * @return {number[]} */ function DoubleNumber(nums) { let eor1 = 0; for (let num of nums) { // 把所有的数值都异或一遍,最后留下的结果就是两个奇数次数异或的结果 eor1 ^= num; } // 由于异或的性质,两个数异或的结果如果某位上是1则说明这两个数原本这位上一位为1,一位为0,只需 let rightOne = eor1 & -eor1; // 重要技巧,与eor1 &(~eor1 + 1)等价,效果为取出最右侧的1 let eor2 = 0; for (let num of nums) { if ((num & rightOne) === 0) { // 数组中的所有数要么这位是0,要么是1,刚好分开了a和b eor2 ^= num; } } return [eor2, eor1 ^ eor2]; } /** * 思路:把所有数都异或起来,最后留下的数就是a和b异或的结果,a和b在某位上肯定是不相等的,假设a在某位上是1 * b在某位上是0,那么这位上异或的结果就是1,而这个一刚好就是异或结果最右侧的1,再来看整个数组的数,由于是 * 二进制,那么所有的数在这位上要么是1要么是0,其中a,和b也分别在这两组数中,现在我们只需要求出a,或者求出b * 那么eor1 ^ a 的结果就是b,eor1 ^ b的结果就是a */ /** * https://leetcode.cn/problems/single-number-ii/ * 给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 * @param {number[]} nums * @return {number} */ var singleNumberOne = function (nums) { let ans = 0; for (let i = 0; i < 32; i++) { let total = 0; // 统计每一位上的和 for (let num of nums) { total += (num >> i) & 1; } // 判断total是否能被三除净,如果除不尽,这个位置就是1 if (total % 3 !== 0) { ans |= 1 << i; } } return ans; }; /** * 思路:我们统计每个数每一位上的数值累加总和,首先我们不考虑只出现一次的那个数,其余出现 * 三次的数累加起来对3求模的结果一定是0,我们再把多出的那个数考虑进去,如果多出的那个数某 * 位上面是0,那么求模的结果还是0,如果是1,那么total求模的结果就是1,而这个1我们要把它放到 * 对应的位置上去,对nums的所有数的每一位都做处理后,所得的结果就是只出现一次的那个数。 */