该篇文章主要讲解了:什么是递归,递归的应用,深拷贝,递归的广度优先和深度优先
原创不易:感觉有帮助的小伙伴可以点个赞,谢谢,有啥不对或者不懂的欢迎评论区留言
首先我们要明白什么是递归,递归就是函数自己调用自己,然后在符合条件时停止调用自己,退出一直调用的循环,在使用递归的时候需要注意的主要有两点:递归的执行过程,递归的退出条件
我以一个比较简单的递归来尽量描述一下 代码如下:小伙伴们可以想想这个打印结果
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)
}
}