算法常用方式汇总(持续更新)


2017/8/8 frontend algorithm

位运算

二进制和十进制转换函数

function tranformBinary(item) {
  var s = null,
    array = null,
    type = typeof item,
    symbol = !/^-/.test(item + '')
  switch (type) {
    case 'number':
      s = Math.abs(item).toString(2)
      if (symbol) {
        return s
      }
      break
    case 'string':
      if (symbol) {
        return parseInt(item, 2)
      }
      s = item.substring(1)
      break
    default:
      return false
  }
  //按位取反
  array = [].map.call(s, function(v) {
    return v ^ 1
  })
  //+1
  array.reduceRight(function(previousValue, value, index, array) {
    var v = previousValue + value == 2
    array[index] = previousValue ^ value
    return +v
  }, 1)
  s = array.join('')
  return type == 'number' ? '-' + s : -parseInt(s, 2)
}
tranformBinary(-12) //"-0100"
tranformBinary('-0100') //-12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

对非整数取整

// 按位与
console.log(5.2 & 5.2) //5
console.log(-5.2 & -1) //-5
console.log(Math.trunc(-5.2) === (-5.2 & -1)) //true
// 按位或
console.log(5.2 | 0) //5
console.log(-5.2 | 0) //-5
console.log(Math.trunc(-5.2) === (-5.2 | 0)) //true
// 按位非
console.log(~~5.2) //5
console.log(~~-5.2) //-5
console.log(Math.trunc(-5.2) === ~~-5.2) //true
1
2
3
4
5
6
7
8
9
10
11
12

判断奇偶数

if ((x & 1) === 1) {
  console.log('x为奇数')
} else {
  console.log('x为偶数')
}
1
2
3
4
5

交换两个数的值

var a = 1,
  b = 2
// 常规方法
var tmp = a
a = b
b = tmp
console.log(a, b) //2 1
// 按位异或方法
a = a ^ b // 假设a,b的原始值分别为a0,b0
b = a ^ b // 等价于 b=a0^b0^b0 ==> b=a0
a = a ^ b // 等价于 a=a0^b0^a0 ==> a=b0
console.log(a, b) //2 1
// 以上可简写为
a ^= b
b ^= a
a ^= b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

实现加法运算

同位相加的运算, 完全可由按位异或(^)代替, 即: x^y. 按位与只有在同位都是 1 的情况下才返回 1, 其他情况均返回 0. 如果对其运算结果再做左移一位的运算, 即: (x&y)<<1, 刚好满足了遇 2 进 1 的场景. 最终的加法运算结果应该还要做一次加法. 如下: x + y = x^y + (x&y)<<1.

这个公式并不完美, 因为它还是使用了加法, 而绕过这个问题有一个前提, 那就是只要 x^y(x&y)<<1 中有一个值为 0 就行了, 这样便不用进行加法运算了. 讲了这么多, 不如看代码.

function add(x, y) {
  const a = x ^ y
  const b = (x & y) << 1
  // a=0则返回b,b=0则返回a,否则继续执行函数
  return (!a && b) || (!b && a) || arguments.callee(a, b)
}
add(12345678, 87654321) //999999999
add(9527, -12) //9515
1
2
3
4
5
6
7
8

计算绝对值

function abs(num) {
  // 保留32二进制中的符号位,根据num的正负性分别返回0或-1
  const x = num >> 31
  // 若num为正数,num^0则返回num本身;若num为负数,则相当于num^-1,此时返回-num-1
  const y = num ^ x
  // 若num为正数,则返回num-0即num;若num为负数则返回-num-1-(-1)即|num|
  return y - x
}
1
2
3
4
5
6
7
8

判断两个数符号是否相同

通常, 比较两个数是否符号相同, 我们使用 x*y>0 来判断即可. 但如果利用按位异或(^), 运算速度将更快.

console.log((-17 ^ 9) > 0) //false
1

对正整数 2n 取模(n 为正整数)

比如 123%8, 实际上就是求一个余数, 并且这个余数还不大于 8, 最大为 7. 然后剩下的就是比较二进制值里, 123 与 7 有几成相似了. 便不难推出公式: x%(1<<n)==x&(1<<n)-1 .

console.log(123 % 8) //3
console.log(123 & ((1 << 3) - 1)) //3
1
2

统计正整数二进制形式中 1 的个数

先判断 n 的奇偶性, 为奇数时计数器增加 1, 然后将 n 右移一位, 重复上面步骤, 直到递归退出.

function getCountForOne(n) {
  return n ? (n & 1) + arguments.callee(n >> 1) : 0
}
getCountForOne(9) //2
1
2
3
4
上次更新: 12/5/2019, 1:42:19 AM