手写算法: 多数元素

169. 多数元素open in new window

解法一:排序

排序数组,如果一个元素出现的频率大于 n / 2, 那么在数组中nums.length / 2的位置就是这个数

// 先排序,再找到中间这个数
var majorityElement = function(nums) {
  nums.sort((a, b) => a - b);
  return nums[Math.floor(nums.length / 2)];
};

解法二:哈希表

循环数组,用哈希表存储数字和对应的个数。当数字出现个数大于n / 2时,返回这个数。

var majorityElement = function(nums) {
  let hash = {};
  for (const num of nums) {
    hash[num] = (hash[num] || 0) + 1;
    if (hash[num] > nums.length >> 1) return num;
  }
};

手写题:

反转二叉树

91. 反转二叉树open in new window

/**
 * @param {Node} node
 * @returns {Node}
 */
function invert(node) {
  if (!node) return null;
  [node.left, node.right] = [invert(node.right), invert(node.left)];
  return node;
}

未排序数列的交集

167. 未排序数列的交集open in new window

传进来的两个数组,是未排序的,而且数组中也可能存在重复的数字。所以输出结果的时候,要注意是否是两个数组共同拥有的元素

解法一:都是 api 大法

利用Set()的特性去重,再筛选

/**
 * @param {any[]} arr1
 * @param {any[]} arr2
 * @returns {any[]}
 */
function getIntersection(arr1, arr2) {
  const a = new Set(arr1);
  const b = new Set(arr2);
  return [...a].filter(item => b.has(item));
}

function getIntersection(arr1, arr2) {
  const arr = arr1.filter(item => arr2.includes(item));
  return [...new Set(arr)];
}

function getIntersection(arr, arr2) {
  const arr = [...new Set(arr1), ...new Set(arr2)];
  let map = new Map();
  let result = [];

  for (const num of arr) {
    map.set(num, (map.get(num) || 0) + 1);
  }

  for (const [key, value] of map.entries()) {
    if (value > 1) {
      result.push(key);
    }
  }
  return result;
}

解法二: 遍历筛选

  1. 先排序,所以相同元素都会在一块连续的内存中

  2. 遍历的时候,如果当前元素与上一个元素相等时,就跳过不处理,继续往后走。因为同一个歌元素统计一次即可。

function getIntersection(arr1, arr2) {
  arr1.sort((a, b) => a - b);
  arr2.sort((a, b) => a - b);

  let n = (m = 0);
  let res = [],
    num;

  while (n < arr1.length && m < arr2.length) {
    if (arr1[n] === arr2[m]) {
      num = arr1[n];
      res.push(num);

      while (n < arr1.length && arr1[n] === num) n++;
      while (m < arr2.length && arr2[m] === num) m++;
    } else if (arr1[n] < arr2[m]) {
      n++;
    } else {
      m++;
    }
  }
  return res;
}

JS 简答题

Function name

65. Function nameopen in new window

var foo = function bar() {
  return 'BFE';
};

console.log(foo());
console.log(bar());

名称 bar 是在函数表达式中使用的, 所以不会在外部声明。所以会报ReferenceError: bar is not defined

Addition vs Unary Plus

14. Addition vs Unary Plusopen in new window

console.log(1 + 2)
console.log(1 + + 2)
console.log(1 + + + 2)
console.log(1 + '2')
console.log(1 + + '2')
console.log('1' + 2)
console.log('1' + + 2)
console.log(1 + true)
console.log(1 + + true)
console.log('1' + true)
console.log('1' + + true)
console.log(1 + null)
console.log(1 + + null)
console.log('1' + null)
console.log('1' + + null)
console.log(1 + undefined)
console.log(1 + + undefined)
console.log('1' + undefined)
console.log('1' + + undefined)
console.log('1' + + + undefined)

补习一下:一元运算符、加减运算符

+ 加法运算符适用于数字和字符串(拼接),如果任何操作数不是数字,则使用+将所有操作数转换成字符串并连接起来。

一元加号运算符(+)在操作数之前,如果尚未将其转换成数字,则尝试先转换成数字。【所以它始终会转换成数字】

✅ 一元运算符的优先级高于加法运算符


看一下 ECMAScript 大宝典:

总结规则

A + B:

  • prim1 = ToPrimitive(A)

  • prim2 = ToPrimitive(B)

  • 如果 prim1 或 prim2 中任何一个是字符串,则返回 ToString(prim1) + ToString(prim2)

  • 否则 ToNumber(prim1) + ToNumber(prim2)

看几个例子,实操一下

Eg1: 1 + undefined

prim1 = ToPrimitive(1) = 1;
prim2 = ToPrimitive(undefined) = undefined;

    // 它们都不是字符串,转换成数字
    ToNumber(1) + ToNumber(undefined)
==> 1 + NaN
==> NaN


Eg2: '1' + undefined

prim1 = ToPrimitive('1') = '1'
prim2 = ToPrimitive(undefined) = undefined;

    // 其中有一个是字符串,所以转换成字符串拼接
    ToString('1') + ToString(undefined)
==> '1' + 'NaN'
==> '1NaN'

Eg3: '1' + +undefined

一元运算: +undefined ==> NaN

prim1 = ToPrimitive('1') = '1'
prim2 = ToPrimitive(NaN) = NaN;

    // 其中有一个是字符串,所以转换成字符串拼接
    ToString('1') + ToString(NaN)
==> '1' + 'NaN'
==> '1NaN'

Eg4: '1' + +null

一元运算: +null ==> 0

prim1 = ToPrimitive('1') = '1'
prim2 = ToPrimitive(0) = 0;

    // 其中有一个是字符串,所以转换成字符串拼接
    ToString('1') + ToString(0)
==> '1' + '0'
==> '10'

再看看平时经常用到的

+1; // 1
+'1'; // 1
+true; // 1
+null; // 0
+undefined; // NaN
+NaN; // NaN
Last Updated:
Contributors: kk