js中的递归,深拷贝,递归的深度优先和广度优先

该篇文章主要讲解了:什么是递归,递归的应用,深拷贝,递归的广度优先和深度优先

原创不易:感觉有帮助的小伙伴可以点个赞,谢谢,有啥不对或者不懂的欢迎评论区留言

首先我们要明白什么是递归,递归就是函数自己调用自己,然后在符合条件时停止调用自己,退出一直调用的循环,在使用递归的时候需要注意的主要有两点:递归的执行过程,递归的退出条件

我以一个比较简单的递归来尽量描述一下 代码如下:小伙伴们可以想想这个打印结果

function recursion(num){
    if(num < 5){
        recursion(num+1)
    }
    console.log(num)
    return
}
recursion(1)

接下来我跟大家一起分析代码:流水账要来了,兄弟们注意...没办法不是特别会画图,只能哔哔

大家记住代码是从上往下从左往右执行的,除非异步,否则只能一行一行来

1:num为1小于5进入判断 调用recursion函数

2:num为2小于5进入判断 调用recursion函数

3:num为3小于5进入判断 调用recursion函数

4:num为4小于5进入判断 调用recursion函数

5:num为5等于5没有进入判断                                          打印num 5

6:num为4的函数中调用的recursion执行完成                  打印num 4  

7:num为4的函数中调用的recursion执行完成                  打印num 3 

8:num为4的函数中调用的recursion执行完成                  打印num 2

9:num为4的函数中调用的recursion执行完成                  打印num 1 

10:有没有一点洋葱圈模型的味道,我记得node还是啥来着,后端的中间件执行跟这个就有点类似。

11:方便理解:其实可以想象成一个栈,这个函数一共执行了5次,第一次的函数放到栈底了,第五次的函数放到栈顶了,先进后出嘛

接下来要上点难度了

深拷贝    其实可以再加一个WeakMap会更好些, 深度优先遍历其实逻辑跟深拷贝一样的

const tree = {
      val: 'a',
      children: [
          {
              val: 'b',
              children: [
                  {
                      val: 'd',
                      children:[],
                 },
                 {
                     val: 'e',
                     children:[],
                 }
             ],
         },
         {
             val: 'c',
             children: [
                 {
                     val: 'f',
                     children:[],
                 },
                 {
                     val: 'g',
                     children:[],
                 }
             ]
         }
     ]
 }

function deepCopy(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
 
    if (obj instanceof Date) {
        return new Date(obj.getTime());
    }
 
    let newObj;
    if(Array.isArray(obj)) {newObj = []}else{newObj = {}}
 
    if (obj instanceof Object) {
        return Object.keys(obj).reduce((newObj, key) => {
            newObj[key] = deepCopy(obj[key]);
            return newObj;
        }, newObj);
    }
}
deepCopy(tree)

以下写的【】里其实都应该有引号的,懒得加了...

1:tree为object走到最后一个判断,通过reduce遍历tree的val和children

2:先是 tree[val] = deepCopy(tree[val])

3:走到第二个函数val不是object直接返回,新的对象,newObj的指针指向了reduce的第二个参数,所以是newObj[val] = a,第二个函数执行结束,并没有继续递归调用

4:回到第一个函数的reduce循环中,既是newObj[children] = deepCopy(tree[children])

5:来到第三个函数,children为对象则走到reduce,开始第一次循环,

6:因为对象的引用关系,newObj[key] 其实就是newObj[children] [val] = deepCopy(tree[children][val]),走到第四个函数因为不是对象,直接返回val  既是newObj[children][0][val] = b

7:继续走,走到了newObj[children][0][children] = deepCopy(treej[children][0][children] )

8:就这样依次类推继续走,走到根节点后,又返回到第一次循环的函数继续走,大体就这样吧,我快蒙了,这是一个理解递归的思路,希望小伙伴可以理解

在写广度优先遍历的时候需要说明一下:

递归的本质与广度优先遍历的特性其实是相冲突的

递归是一种函数调用自身的机制,它通常用于深度优先遍历,因为在深度优先遍历中,我们需要先处理完一个节点的所有子节点(即深入到树的最深处),然后再回溯到上一个节点继续处理其未被处理的子节点。递归能够自然地处理这种回溯和深入的过程。

然而,广度优先遍历(BFS)是按照层次顺序遍历树的,即先处理完同一层的所有节点,然后再处理下一层的节点。为了实现这种层次顺序的遍历,我们其实应该使用一个队列数据结构来存储待处理的节点。队列的特性是先进先出,这正好符合广度优先遍历的需求:我们先将同一层的所有节点入队,然后依次出队并处理它们,同时将它们的子节点入队,以便在下一轮中处理。

例:

const tree = {
      val: 'a',
      children: [
          {
              val: 'b',
              children: [
                  {
                      val: 'd',
                      children:[],
                 },
                 {
                     val: 'e',
                     children:[],
                 }
             ],
         },
         {
             val: 'c',
             children: [
                 {
                     val: 'f',
                     children:[],
                 },
                 {
                     val: 'g',
                     children:[],
                 }
             ]
         }
     ]
 } 

function bfs(root) {  
    if (!root) return; // 如果根节点为空,直接返回  
    let queue = [root]; // 初始化队列,将根节点入队  
  
    while (queue.length > 0) {  
        let current = queue.shift(); // 取出队首节点  
        console.log(current.val); // 处理节点(这里只是打印值)  
  
        // 将当前节点的所有子节点入队  
        if (Array.isArray(current.children) && current.children.length > 0) {  
            queue.push(...current.children);  
        }  
    }  
}  
  
bfs(tree); // 调用广度优先遍历函数

这篇文章主要讲的是递归,所以我通过递归模拟了一下广度优先遍历

const tree = {
      val: 'a',
      children: [
          {
              val: 'b',
              children: [
                  {
                      val: 'd',
                      children:[],
                 },
                 {
                     val: 'e',
                     children:[],
                 }
             ],
         },
         {
             val: 'c',
             children: [
                 {
                     val: 'f',
                     children:[],
                 },
                 {
                     val: 'g',
                     children:[],
                 }
             ]
         }
     ]
 }

 function guang(data){
    let arr;
    if(!Array.isArray(data)) {arr = [data]}else{arr = data}
    const children = []
    arr.forEach((item)=>{
        console.log(item.val);
        children.push(...item.children)
    })
    if(children.length){
        guang(children)
    }
 }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值