Javascript算法练习(七)

本文介绍了在JavaScript中使用递归和闭包实现斐波那契数列求和的方法,包括直接递归、闭包以及for循环三种方式,并分析了它们的效率。此外,还探讨了数值进制之间的转换,提供了解决方案的代码实现。
摘要由CSDN通过智能技术生成

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代码仓库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若叶岂知秋vip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值