动动动手动动脑才有提高!
分析: string.repeat(n) 方法会返回字符串重复值,如 let a = "abc".repeat(2),返回的是 “abcabc”。
常规方法: 采用for循环,每次累加n次初始值,则获得最终结果。
缺点:时间复杂度为o(n),有很大优化空间。这种方法简单易懂,新手都会。
优化思路:
1. 每次累加前一次的结果,如 第一次累加初始值得到 ‘abcabc’(重复2),再累加一次得到‘abcabcabcabc’(重复4),实际上就是以2的幂次方来累加,这样重复次数很大的情况下,可以明显减少累加次数
2. 我们可以找到最接近目标次数的最大幂次方,如 n = 11, 需要结果得出 repeat 11次的结果。那最接近11的2的倍数是2的3次方,然后剩余3次repeat。然后再对3继续分解为2的1次方和2的0次方。因此:
那么: 11可以分解为 = 2的3次方 + 2的1次方 + 2 的0 次方
上代码吧!Show me the code
方法一:通过递归逐步计算每块的值。2的3次方需要累加3次,2的1次方需要累加1次,2的0次方直接加1次。一共执行了5次,比循环11次时间复杂度缩小了一半多。
String.prototype.myRepeat = function(num){
let str = this.valueOf(); //初始值
let string = "";
if(num == 0) return "" //边界条件:返回空
if(num == 1) return str //边界条件:返回1次
let times = Math.floor(Math.log2(num)) //计算2为底num的对数,再取整,得到最大2次幂
let left = num - Math.pow(2,times) //计算剩余的需要进行多少次repeat
for(let i = 0; i <= times; i++ ){
if(!string){
string += str //初始值
}else{
string += string //累加
}
}
let more = str.myRepeat(left) //剩余的再执行repeat函数
return string+more
}
let str = "9"
let long = str.myRepeat(20) //repeat 20次,已优化到循环执行8次
console.log(long)
console.log(long.length)
缺点:执行过程从次方最大开始计算,每块会分别进行计算,计算过程有冗余。计算高次幂的同时其实已经把低次幂的计算了一次。(不推荐)
方法二:对方法一的循环进行优化(推荐),每次循环判断本次循环是否需要加上累加的str,并再对str进行翻倍累加,这样可以充分利用每次产生的2的次方倍数字符串。
1、循环条件:每次将num处以2并取整,如果num <= 0 则跳出循环;
2、每次循环判断目标字符串string 是否需要加当前的str累计倍数的值:num%2 取余,如果不为0,则本次需要加上累加值。
String.prototype.myRepeat = function(num){
let str = this.toString();
let string = "";
if(num == 0) return ""
if(num == 1) return str
while(num > 0){
if(num%2 != 0){
string += str
}
num = Math.floor(num/2)
str += str
}
return string
}
方法三:官方源码:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
核心算法(已去除容错判断及边界条件判断):划重点 —— 二进制 位与运算 & 、 移位运算 >>>
String.prototype.repeat = function(count) {
var str = '' + this;
count = +count;
count = Math.floor(count);
var rpt = '';
for (;;) {
if ((count & 1) == 1) { //位与运算
rpt += str;
}
count >>>= 1; //移位运算 相当于除以2取整
if (count == 0) {
break;
}
str += str;
}
return rpt;
}
分析:
1. (count & 1) : count是奇数 返回 1,偶数返回0,位与运算比较 000101位与000001 返回 000001, 则为1,000110位与000001 返回000000,则为0。
2、>>> :二进制所有数位整体右移, >>> 1则为移动移位,实际效果为除以2取整,(>>>= 用法类似于 +=)
3、还是前面的原理以20为例,分解公式为 20 = 2的4次方 + 2的2次方;
4、源码的方法比较抽象,太高级了(实际执行循环次数为4次,比第一种方案再提升了一倍效率)
执行 rpt += str的时候,str每次循环之后翻倍(str+=str),如果第一次循环执行到这句,实际效果是rpt(初始值为“”)加上2的0次方(str的上次累加值,初始值为this),第n次循环循环执行到这句的时候,实际效果rpt加2的n次方。
官方方法简便效率很高。