算法的核心是创建问题抽象的模型和明确求解目标,之后可以根据具体的问题选择不同的模式和方法完成算法的设计。
一个例子:判断是否为4的幂
给定一个整数,写一个函数来判断它是否是4的幂次方。如果是,返回true
;否则,返回false
。
菜鸡入门
- 负数直接排除
- 循环除4,只要有一次不能整除就返回false
- 循环完成返回true
var isPowerOfFour = function (n) { if (n <= 0) return false; while (n > 1) { if (n % 4) return false; n /= 4; } return true; }; 复制代码
位运算
将上一步的运算换为位运算:
n % 4
可以用n & 0b11
代替,按位与二进制11n /= 4
可以用n >>= 2
代替,右移两位
var isPowerOfFour = function (n) { if (n <= 0) return false; while (n > 1) { if (n & 0b11) return false; n >>>= 2; } return true; }; 复制代码
位运算进阶
是4的幂的前提是2的幂,所以我们先进行两个判断:
- 大于0
n & (n-1) === 0
如2的三次幂8,二进制为1000,7的二进制为0111,所以按位与的结果为0,进一步再判断是否为4的幂:
n & 0xAAAAAAAA === 0
观察4的幂的二进制:
可以看出4的幂的奇数位都为1,而0xA
为二进制0b1010
,所以将它们进行按位与,如果结果为0则证明其为4的幂。
var isPowerOfFour = function (n) { return n > 0 && (n & (n-1)) === 0 && (n & 0xAAAAAAAA) === 0; }; 复制代码
巧用正则
既然上一步我们已经发现了4的幂的二进制特点:
- 1开头
- 后面偶数个0
那么直接利用JS的特性写一个正则就可以了:
var isPowerOfFour = function (n) { n = n.toString(2); return /^1(?:00)*$/.test(n); }; 复制代码
更多位运算
lowbit操作用于截取一个数字的二进制最后一个1后面的所有位:
function lowbit(n) { return n & -n; } 复制代码
或者你也可以用只保留最后一个1及其之前的所有位:
function lowbit(n) { return n & (n-1); } 复制代码
求n的第k位二进制:
function k2(n) { return n >> k & 1; } 复制代码
- 使用左移得出二的次幂:
console.log(1 << 2) // 1*4,2的2次方 console.log(2 << 4) // 2*16 复制代码
- 使用
^
切换0,1:
toggle ^= 1 复制代码
- 使用
&
判断奇偶性:
console.log(5 & 1) // 1,为奇数 console.log(6 & 1) // 0,为偶数 复制代码
- 使用
^
来完成值交换,比解构赋值快:
[a, b] = [b, a] a ^= b b ^= a a ^= b