关于JavaScript中递归的用法(包你看完后想再不懂闭包都难)

关于JavaScript中递归的用法(包你看完后想再不懂闭包都难)

递归:对于递归,最直接的说法,就是函数中又调用函数自己,此时就是递归,但是一定不要忘记,递归一定要有结束条件的

在你看这篇文章之前,你可以先看看下面这三个有关于递归的问题,并且带着这三个问题来看这篇文章,这样相信你会对递归有更加深刻的理解。(由于自己还不是JavaScript的大佬,所有如有错误,还请大家能够指出小编的不足,小编再次感谢各位)

     1)假如楼梯有n个台阶,每次可以走1个或2个台阶,请问走完这n个台阶有几种走法?
     2)如何用递归思想实现深拷贝?
     3)如何用递归思想实现数组的扁平化?

想必在你看完这三个问题后,你会联想到之前所学到的递归,不管你现在心里是否已有了答案,都不要紧,下面我将从递归的简单用法,到递归案例,再是一些大家在面试中容易遇到的一些面试题做一个简单的分享…

一.若没有结束条件,将形成死循环

function f1() {
            console.log("hello,world!");
            f1();
        }
        f1();//浏览器崩溃,因为没有结束条件——死循环

那么我们应该如何改进,让它成为真正的递归?请看下面的代码

var i = 0;
        function f1() {
            i++;
            if (i < 5) {
                f1();
            }
            console.log("递归!");
        };
        f1();

答案很简单,当我们设置了结束条件后,这样就完成了一个简单的递归了

了解了递归的基本用法后,我们再来看一些递归的经典案例

二.案例:

递归实现:求n个数字的和 n=5 ------->5+4+3+2+1
            function getSum(n){
                return n==1 ? 1 : n+getSum(n-1) 
            }
            console.log(getSum(5));
            
递归实现:求一个数字各个位数上的数字的和:   123 --->6 ---1+2+3
           function getSum(n){
               if( n<=9 ){
                   return n
               }
               return (n%10) + getSum(parseInt(n/10))
           }
           console.log(getSum(123));
递归实现:求斐波那契数列:112358132134
        function getFib(x) {
            if(x==1||x==2){
                return 1
            }
            return getFib(x-1)+getFib(x-2);
        }
        console.log(getFib(1));
递归实现: 有一个细胞 每一个小时分裂一次,一次分裂一个子细胞,第三个小时后会死亡。那么第n个小时候有多少细胞 ?
       
        function cell(hour) {
            function livecell(hour) { //活着的
                if (hour <= 4) {
                    return Math.pow(2, hour - 1)
                } else {
                    return livecell(hour - 1) * 2 - diecell(hour)
                }
            }
            function diecell(hour) { //死亡
                if (hour <= 4) {
                    return 0
                } else {
                    return livecell(hour - 3) - diecell(hour - 1) - diecell(hour - 2)
                }
            }
            return livecell(hour)
        }
        console.log(cell(5))

相信这些案例,在大家最开始学习递归的时候就已经遇到过,也是最基本的几个案例,在这里我们就不再过多的进行讨论。我们最重点的部分来了,就是面试中会遇到的递归题。

1.大家先看一下下面的代码,思考一下会打印出什么结果?

 function fact(num) {
            if (num <= 1) {
                return 1;
            } else {
                return num * fact(num - 1);
            }
        }
        var antherFact = fact;
        fact = null;
        console.log(antherFact(4));

首先很容易看的出来,这段代码块运用了递归思想,来想实现的一个阶乘。

想必会有部分小伙伴会认为打印出正确的4的阶乘,其实并不然。下面就是这段代码的执行结果。
在这里插入图片描述

原因其实很简单,就是当fact函数想在自己函数内部在调用自己的时候,但是我们在此函数付给了antherFact 后,进行了fact = null 的处理。

下面将是这段代码的解决方案,就是使用 arguments.callee(返回正在被执行的对现象)

function fact(num) {
            if (num <= 1) {
                return 1;
            } else {
                return num * arguments.callee(num - 1);
            }
        }
        var antherFact = fact;
        fact = null;
        console.log(antherFact(4));

2.数组的扁平化(递归)

var arr = [1,2,3,[4,5],[6,[7,8]]]
        var newArr = []
        function getArray(arr){
            for(var i = 0; i<arr.length ; i++){
                if( arr[i].constructor === Array){
                    getArray(arr[i]) //当我们判断出数组的项为数组类型时,就在将其调用getArray函数进行偏平
                }else{
                    newArr.push(arr[i])
                }
            }
        }
        getArray(arr)
        console.log(newArr);

3.冒泡排序

相信大家对冒泡排序已经很熟悉,这里我就不再讲解冒泡排序的实现思路。下面我们先看看用常规的方法是怎么实现排序的。

var arr = [5, 3, 2, 12, 11, 8, 99];
        function bubbleSort(arr) {
            if (arr.length  < 2)
                return arr;
            for (var i = 0; i < arr.length - 1; i++) {
                let shouldSort = false;
                for (var k = 0; k < arr.length - 1 - i; k++) {
                    if (arr[k] > arr[k + 1]) {
                        var temp = arr[k];
                        arr[k] = arr[k + 1];
                        arr[k + 1] = temp;
                        shouldSort = true;
                    }
                }
                if (!shouldSort) break;
            }
            return arr;
        }
        var result = bubbleSort(arr);
        console.log(result);

那么如何运用递归呢?我们知道,冒泡排序是对数组进行了双重for循环遍历比较,那么这时候递归就能在这排上用了,也就是只用进行一次for循环,再加上递归,也能实现我们的冒泡排序。

function Stor(arr,size){
            if(size <2 ){  //这里须要对我们的递归设置结束条件
                return arr
            }else{
                for(var i = 0 ; i<size-1 ; i++){
                    if(arr[i] > arr[i+1]){
                        var temp = arr[i]
                        arr[i] = arr[i+1]
                        arr[i+1] = temp
                    }
                }
                return Stor(arr,size-1)
            }
        }
        console.log(Stor(arr,arr.length));

4.与冒泡类似,下面是运用递归实现的快速排序:

//方式1
function QSort(arr) {  
     //如果数组<=1,则直接返回
     if(arr.length <= 1){
         return arr;
    }
     //找基准,并把基准从原数组删除
     var pivot = arr.splice(0, 1)[0];
     //定义左右数组
     var left = [];
     var right = [];

     //比基准小的放在left,比基准大的放在right
     for(var i = 0; i < arr.length; i++){
         if(arr[i] <= pivot){
             left.push(arr[i]);
         }
         else{
             right.push(arr[i]);
         }
     }
     //递归
     return QSort(left).concat([pivot],QSort(right));
}
console.log(QSort([7,2, 3, 4, 5, 10, 7, 8, 2,21]));

//方式2
function QSort2(arr, left, right) {  
    if(left < right){
        //找基数第一趟排序后的位置
        let pivot = partion(arr,left,right);
        //递归排序左右区间
        QSort2(arr, left, pivot - 1);
        QSort2(arr, pivot + 1, right);
    }
    return arr;
}
//快排第一趟
function partion(arr, left, right) {  
    //第一个元素作为基数
    let pivotVal = arr[left];
        pivot = left;
    while(left<right){
        while(right>left && arr[right]>=pivotVal){
          right--;
        }
        [arr[left], arr[right]] = [arr[right], arr[left]];

        while(left<right && arr[left]<=pivotVal){
            left++;
        }
        [arr[left], arr[right]] = [arr[right], arr[left]];
    }
    return left;
}
var arr = [7,2, 3, 4, 5, 10, 7, 8, 2,21];
console.log(QSort2(arr, 0, arr.length-1));

5.深浅拷贝

深浅拷贝大家应该也不陌生吧,也就是对一个对象进行拷贝的过程,区别就在于,如果对象没有引用类型的时候进行的拷贝,称为浅拷贝,反之,当对对象中的引用类型一同拷贝的就,并且新对象和老对象不再公用一个引用地址,就是深拷贝,也可以理解为对浅拷贝的一个递归过程,下面是我编写的浅拷贝和深拷贝的实现过程及说明。

var obj = {
            name: 'liuq',
            age: 19,
            address: {
                home: 'SC'
            }
        };
        var newobj = {};
        console.log(obj);

       // 浅拷贝
        function extend(p, c) { 
            var c = c || {};

            for (var i in p) {
                c[i] = p[i];
            }
        }
        extend(obj, newobj);
        newobj.address.home = 'BJ'
        console.log(obj);


        //深拷贝 ==> 对浅拷贝的递归
        function extendDeeply(p, c) { 
            var c = c || {};
            for (var i in p) {
                if (typeof p[i] === 'object') {
                    // 引用类型需要递归实现深拷贝
                    c[i] = (p[i].constructor === Array) ? [] : {}
                    extendDeeply(p[i], c[i]);
                } else {
                    // 非引用类型直接复制即可
                    c[i] = p[i];
                }
            }
        }
        extendDeeply(obj, newobj);
        newobj.address.home = 'BJ'
        console.log(obj);

最后,小编祝你学有所成,感谢你的支持!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值