Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
字符串补位leftpad
说起二分,一个典型的事件就是left-pad事件。不了解该事件的可以自行搜索,我们本文的重点是二分法实现一个left-pad功能。leftpad是一个给字符串左侧补位的功能,给一个字符串和一个长度,当字符串长度不够时,使用指定字符在它左侧给它补齐到给的的长度
leftpad(str, len, ch)
// str: 指定字符串
// len:指定长度
// ch:补位的字符
示例:
leftpad('hello', 10, 'a')
输出: 'aaaaahello'
解释: 'hello'字符长度为5,不满足指定长度10,需要用'a'字符在'hello'的左侧进行5次补位,即5个'a'拼接上'hello'
一、npm中的leftpad的紧急代替版
module.exports = leftpad;
function leftpad (str, len, ch) {
str = String(str);
var i = -1;
if (!ch && ch !== 0) ch = ' ';
len = len - str.length;
while (++i < len) {
str = ch + str;
}
return str;
}
上面的方法我们可以知道是直接循环拼接字符串的,复杂度为0(n)
二、二分法实现leftpad
二分法思路分析:
-
先确定需要补位的位数
let length = len = str.length。这时确定了需要补位length个ch字符 -
定义补位的字符串total,对length进行循环判断,每次length除以2,ch每次累加自己,total进行累加ch。
/** * len=10时,需要拼接5次 * 方法一:循环拼接5次 * 方法二:记一个缓存值total: * 00 * 00 + 00 + 0 * 需要拼接2次 */ /** * len=25时,需要拼接20次 * 方法一:循环拼接20次 * 方法二:记一个缓存值total: * 00 * 0000 * 00000000 + 00000000 + 0000 * 只拼接3次 */ -
当length为1时,无法再二分,直接累加str输出
具体实现:
function leftpad2(str, len, ch) {
let length = len - str.length
// 二分法
let total='', cache = ch;
while (length) {
// 缓存结果*2处理,剩下的长度/2处理
cache += cache
length = parseInt(length / 2)
// 余1时将这1个值累加
if (length % 2 === 1) {
total += cache
}
// 最后length可以整除2,无法二分的时候做拼接return处理
if (length === 1) {
return total + str
}
}
}
当循环次数n量级越大时,两种方法的差异越明显:
// O(n)复杂度执行时间
console.time('leftpad1')
for(let i=0; i<10000; i++) {
leftpad1('holle', 10000, 'a')
}
console.timeEnd('leftpad1')
// 二分法log2(n)复杂度执行时间
console.time('leftpad2')
for(let i=0; i<10000; i++) {
leftpad2('holle', 10000, 'a')
}
console.timeEnd('leftpad2')
输出结果:
可以看出当量级上去后,运行时间出现了很大差异,当n越大,运行时间都不是一个数量级了。
三、位运算的优化
其中取余%和除法/可以优化一下
function leftpad3(str, len, ch) {
let length = len = str.length
// 二分法
let total = ''
while(length) {
// &运算符:按位与(本质上是二进制下的数按位对齐取与操作)
if (length & 1) {
total+=ch
}
if (length===1) {
return total+str
}
ch+=ch
// >> 向右位移1位(二进数右位移1位相当于除以2)
length = length >> 1
}
}
优化以后当循环和len达到百万的量级时,我们就能看出leftpad3的时间小于leftpad2,但是总体的时间还是同一个量级的。
【拓展】
-
按位与:
例如:25 & 3 = 1 25 = 0001 1001 3 = 0000 0011 按位取与操作为: res= 0000 0001对于上面的
if (length & 1),任何数和1按位与为真就说明该数的二进制的最后一位必须是1,二进制的最后一位为1那不就是无法整除2余1吗所以
if (length & 1)和if (length % 2 === 1)是一样的。大家可以自己举例几个数和1的按位与,手写下试试哦!
-
有符号右移:右移几位就是除以2的几次方
二进制状态下向右位移1位相当于除以2。右移2位相当于除以4。例如64右移5位结果就是2。