递归在实际项目中的几种场景应用

149 阅读4分钟

1. 什么是递归?

递归是指一个函数在其定义中调用自身的过程。它通过将一个大问题分解为一个或多个相同类型的子问题来解决问题。每次递归调用都会将问题规模减小,直到达到基本情况(递归终止条件),然后逐步返回结果,最终解决整个问题。

  • 核心思想:将一个大问题分解为多个相同或相似的小问题,直到问题足够简单,可以直接解决。
  • 递归的两个关键部分
    1. 基线条件(Base Case) :递归终止的条件。
    2. 递归条件(Recursive Case) :函数调用自身的条件。

2. 递归和迭代的区别

什么是迭代

迭代是通过循环结构重复执行一段代码来解决问题。它通过迭代变量的更新和循环条件的判断来控制循环的执行次数。迭代通常使用循环语句(如 forwhile)来实现

区别
  1. 实现方式:递归是通过函数调用自身来实现的,而迭代是通过循环结构来实现的。

  2. 控制流程:递归是通过不断地调用自身来处理子问题,直到达到基本情况并返回结果。迭代是通过循环结构来重复执行一段代码,直到满足循环条件退出循环。

  3. 内存占用:递归可能会占用更多的内存空间,因为每次递归调用都需要保存函数的上下文和局部变量。而迭代通常只需要保存循环变量和少量的临时变量。

  4. 可读性和理解性:递归可以更直观地表达问题的解决思路,特别是对于涉及到递归定义的问题。但是过度的递归调用可能会导致代码难以理解和调试。迭代通常更直观和易于理解。

选择递归还是迭代取决于具体的问题和算法。有些问题更适合使用递归,因为它们可以自然地分解为子问题。而有些问题则更适合使用迭代,因为它们可以通过循环结构更高效地解决。

递归在实际项目中的几种场景应用

先定义一个数组对象list`是一个Array<Object>类型的数组,它可以包含check, id, name ,children等属性的对象,属性children的结构和list 结构是相同的

let list = [
    {
        check: true,
        id: 1,
        name: 1,
        children: [
            { id: 11, name: 11, children: [{ id: 111, check: true, name: 111, children: [] }] },
            { id: 12, name: 12, children: [] },
        ],
    },
    { id: 2, name: 2, children: [] },
    { id: 3, name: 3, check: true, children: [] },
]

场景一,常用场景 定义getXXX递归查找匹配某个id的对象, 例如查找数组list中id=111的对象name是什么

let getXXX = (list, cuId) => {
    for (const item of list) {
        if (item.id == cuId) {
            return item.id
        }
        if (item.children?.length) {
            const temp = getXXX(item.children, cuId)
            if (temp) {
                return temp
            }
        }
    }
    return null
}

场景二,#### 定义getXXX递归给list中所有对象加一个字段 并且这个字段要包含所有父级对象的id, 例如 给所有对象 加一个字段allId ,allId=father1Id+father2Id+father3Id+当前Id

需要在函数里用到一个参数extra来记录父级id

//借助一个参数extra来记录父级id
let getXXX = (list, extra='') => {
    for (const item of list) {
    extra=extra+ "/"+item.id
        item.allId=extra
        if (item.children?.length) {
            getXXX(item.children, extra)
        }
    }
}
getXXX(list)
console.log(list)

list 结果打印如下

[
    {
        id: 1,
        name: 1,
        children: [
            {
                id: 11,
                name: 11,
                children: [
                    {
                        id: 111,
                        name: 111,
                        children: [],
                        allId: '/1/11/111',
                    },
                ],
                allId: '/1/11',
            },
            {
                id: 12,
                name: 12,
                children: [],
                allId: '/1/12',
            },
        ],

        allId: '/1',
    },
    {
        id: 2,
        name: 2,
        children: [],

        allId: '/2',
    },
    {
        id: 2,
        name: 2,
        children: [],
        allId: '/2',
    },
]

场景三,getXXX递归获取list中指定条件所有的对象合集,例如 获取list下对象中字段check值为true 的name合集,借助参数extra来记录满足条件的对象 并在递归完成后return这个参数

//借助参数extra来记录满足条件的对象
let getXXX = (list, extra = []) => {
    for (const item of list) {
        if (item.check) {
            extra.push(item.name)
        }
        if (item.children?.length) {
            getXXX(item.children,extra)
        }
    }
    return extra
}
console.log(getXXX(list))
场景四 混合使用 (场景一场景三结合使用)
// 递归查找
let getTel = (list, cuId) => {
    for (const item of list) {
        if (item.id == cuId) {
            for (const user of item.deptUsers) {
                if (user.userId == gUser.userId) {
                    if (user.admin == true) {
                        return getTelAdmin([item])
                    } else {
                        return [user.userTel]
                    }
                }
            }
        }
        if (item.child?.length) {
            const temp = getTel(item.child, cuId)
            if (temp) {
                return temp
            }
        }
    }
    return null
}
// 递归叠加找到所有的tel 
let getTelAdmin = (list, adminTels = []) => {
    for (const item of list) {
        for (const user of item.deptUsers) {
            adminTels.push(user.userTel)
        }
        if (item.child?.length) {
            getTelAdmin(item.child, adminTels)
        }
    }
    return adminTels
}