Javascript算法练习(七)

来源:互联网 发布:清华大学研究生 知乎 编辑:程序博客网 时间:2024/05/28 11:50

Javascript算法练习(七)

这两天碰到个小问题,跟斐波那契数列有关,顺便也回顾下回调函数的使用。


sumOddFibonacciNumber: 得到小于number的所有斐波那契数的和

  • 递归的三种方式

    1. 直接递归法:容易引起内存泄漏,效率底下,慎用
    2. 闭包: 能很好的练习js闭包用法,虽然效率不是最高,但是推荐使用
    3. for循环 效率最高,简单粗暴直观

    var count = 0; // 测试三种方式的计算次数

  • 直接递归法

    // 基础递归方法,没什么好说的,很简单的操作var fibonacciBasic = function (number) {    if (number == 0) {        return 0;    } else if (number == 1) {        return 1;    } else {        count++;        return fibonacciBasic(number - 1) + fibonacciBasic(number - 2);    }};
  • 闭包,即通过函数内部定义闭包,并且使用相应的数组或对象去缓存已经计算出的值,方便下次直接提取,能大大减少计算的次数

    // 闭包实现var fibonacciClosure = (function () {    // 这个用来缓存计算出的值,方便下次直接提取使用    var res = [0, 1];    return function (number) {        if (isNaN(number) || number < 0) return;        // 测试用,计算回调次数        count++;        // 判断该值是否曾经计算且保存过,有则直接取出来使用,没有则进行下一步,        // 并且将新值缓存起来        if (res[number] || res[number] === 0)) {            console.log("exist, res[" + number + "] = " + res[number]);            return res[number];        } else {            res[number] = fibonacciClosure(number - 1) + fibonacciClosure(number - 2);            return res[number];        }    };})();
  • 通过for循环方式去处理,效率最高,且不存在内存泄漏的问题,如果不喜欢用闭包,可以使用这种方式,并且此方式用来处理相加比较容易,后面有通过这个扩展来处理所有斐波那契数值的和

    // 循环var fibonacciCircle = function (number) {    if (number == 0) {        return 0;    } else if (number == 1) {        return 1;    } else {        var x = 0;  // 保存第一个值        var y = 1;  // 保存第二个值        var z = 2;  // 保存第一个和第二个值的和        for (var i = 2; i <= number; i++) {            count++;            // 进行值替换            z = y + x;            y = x;            x = z;        }        return z;    }}
  • 也顺便看看上面三种执行的次数吧,记忆会更深点以下是分别计算:第10,20,30个的数据的count值;通过下面的对比可以看出三种方式,第一种是最糟糕的,必须严厉禁止使用,后面两个差距并不是非常明显,闭包的实现,是在牺牲了一定的内存上去换的运行时间,所以可以根据个人喜好选择。

    • 第10个fibonacci:
      • [Basic]value == 55, count == 88;
      • [Closure]value == 55, count == 19;
      • [Circle]value == 55, count == 10;
    • 第20个fibonacci:
      • [Basic]value == 6765, count == 10945;
      • [Closure]value == 6765, count == 39;
      • [Circle]value == 6765, count == 20;
    • 第30个fibonacci:
      • [Basic]value == 832040, count == 1346268;
      • [Closure]value == 832040, count == 59;
      • [Circle]value == 832040, count == 30;

  • 通过for循环扩展,处理所有斐波那契数值的奇数值的和,最后需要用到reduce方法去处理最后得到的数组,将它们的元素统统加起来

    // 得到小于number的所有斐波那契数的和function sumOddFibonacciNumber(number) {    // 要求传入的参数为合法的数值类型    if (isNaN(number) || number < 0) return;    var total = [0, 1];  // 保存计算出的fibonacci数    if (number == 0 || number == 1) {        return 1;    } else {        var x = 0;  // 保存第一个值        var y = 1;  // 保存第二个值        var z = 1;  // 保存第一个和第二个值的和        for (var i = 1; i <= number; i++) {            if (z > number) break;            console.log("total = " + total + ", z = " + z);            // 过滤调第一个z的值,防止把它的初始值保存进去了            if (i > 1 && (z % 2 != 0)) {                total.push(z);            }            // 值替换和累加操作            z = y + x;            y = x;            x = z;        }        // 上述操作结束后,total就保存了小于number的所有fibonacci数值        // 然后将他们全部相加,得到我们想要的值        return total.reduce(function(preV, currV, currIndex, array){            return preV + currV;        });    }};

数值进制之间的转换

  • function decimalToOther(number, type); number:要转换的数字,type:转成的目标进制;
  • function otherToDecimal(numberStr, type); numberStr:转化成10进制的字符串或者数字也可以,如果是数字直接返回,是字符串则需要进一步转换,type:传入的第一个参数的进制类型(后续考虑优化去掉这个参数,直接去方法里面判断);
  • function prefixHandler(bitArr, type, method); 辅助方法,前两个方法的前缀处理,bitArr:每个数值位或进制用split(“”);字符串分割后的数组;type:进制类型,数值型;method:处理方式,0-不处理,1-添加前缀,2-删除前缀。

  • 进制之间的转换原理还是挺简单的,就是用数字去除要转换的进制,取得余数作为转换后的相应的位数,除数作为下一次循环的数字。

  • 方法实现的步骤或注意点

    1. 各种进制的前缀:如果是二进制则需要考虑前面是否需要补0,8进制前缀:’0’,16进制’0x’;
    2. 16进制的10-15的转换,用数组处理[‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’];
    3. 在其他进制转成10进制时,需要用到reduce处理。
  • function decimalToOther(number, type);代码实现

    NumberHandler.prototype.decimalToOther = function (number, type) {    // 保证参数都是数字    // type一般取值:2, 8, 10, 16    if (isNaN(number) || isNaN(type) || number < 0) return;    if (type === 10) return number;    var hexAlpha = ['A', 'B', 'C', 'D', 'E', 'F'];    var bitArr = [];    // 存储计算得到的每一位上的数    var rema = 0;       // 保存余数    var prefix = "";    // 每种进制的前缀,如:16进制的"0x", 8进制的'0'等    while (number !== 0) {        // 取余数保存,用来组合成最后进制字符串        rema = number % type;        // 当type == 16时,需要处理10-15到ABCDEF的转换        if (type === 16 && rema > 9 && rema < 16) {            rema = hexAlpha[rema - 10];        }          bitArr.unshift(rema);        // 取除数,进行下一个循环        number = Math.floor(number / type);    }    // 添加前缀    this.prefixHandler(bitArr, type, 1);    return bitArr.join("");}
  • function otherToDecimal(numberStr, type);代码实现

    NumberHandler.prototype.otherToDecimal = function (numberStr, type) {    // 如果是十进制就直接返回    if (type === 10 && !isNaN(numberStr)) return numberStr;    // 保证除十进制外传进来的都是其他进制的字符串形式    if (typeof numberStr != "string") return;    // 将numberStr变成数组    var bitArr = numberStr.split("");    var hexAlpha = ['A', 'B', 'C', 'D', 'E', 'F'];    var alphaIdx = 0;    // 去掉前缀    this.prefixHandler(bitArr, type, 2);    // 16进制转换成数字    if (type === 16) {        bitArr.map(function (value, index) {            alphaIdx = hexAlpha.indexOf(value);            if (alphaIdx != -1) {                // 用数字去替换字母                bitArr.splice(index, 1, 10 + alphaIdx);            }        });    }    var len = bitArr.length;    return bitArr.reduce(function (preV, currV, currIndex, array) {        return preV + parseInt(currV) * Math.pow(type, len - currIndex - 1);    }, 0);}
  • function prefixHandler(bitArr, type, method); 前缀处理

    NumberHandler.prototype.prefixHandler = function (bitArr, type, method) {    // method: 0 - 什么都不做,1 - 添加,2 - 删除    if (!bitArr || isNaN(type)) return;    // 如果method == 0, 什么都不做    if (method === 0) return;    var prefix = "";    var count = 0;    switch (type) {        case 2:            if (method === 1) {                 prefix = '0'; // 2进制前位补0                while (bitArr.length < 8) {                    bitArr.unshift(prefix);                }            }            break;        case 10:            break;        case 8: // 八进制则删除最左边的'0'            prefix = '0';            count = 1; // 删除一位            break;        case 16: // 十六进制删除'0x'            prefix = '0x';            count = 2; // 删除两位            break;        default:            break;    }    // 2进制单独在switch里处理    if (type === 2) return;    // 1 - 添加,2 - 删除,0 - 什么都不做    method === 1 ? bitArr.unshift(prefix)                 : bitArr.splice(0, count);    return bitArr;}
  • 结束语
           妮妲太凶悍了,只怪暴风雨来的太凶猛,却还是不够凶猛啊,公司原定上午“在家办公的”,下午回公司上班,结果下午还在持续爆发之中,弄的周六去补下午的班,在屋里听朋友说公司直接“在家办公一整天,哈哈!!”,额……,别人家的公司,好熟悉的词!!!不过公司还是很不错的,至少没扣薪水是吧,^_^!! 要努力成为勇于,敢于,善于,爱好加班的好童鞋嘛,才是未来公司的好班长不是,嘎嘎嘎,Come on, come on, baby!!!

github代码仓库

0 0