使用javascript写一个函数检测循环依赖

154 阅读2分钟

1. 题目内容

前几天在刷题的时候,遇到下面这个题,自己卡了好久,特此记录一下。 题目内容如下

有一个数据结构如下, 请实现一个函数, 检查依赖中是否出现循环依赖,而且要求可以检查子依赖

const dependencies = [
    {
        name: "a",
        dependencies: 'b'

    },
    {
        name: "b",
        dependencies: 'c'

    },
    {
        name: "c",
        dependencies: [{
           name: "d",
           dependencies: 'a'
        }]
    },
]

2. 解题思路

递归解法

首先用人脑模拟一下遍历过程, 从第一个元素开始遍历,

  1. 那么a依赖b, b依赖c, 此时依赖链条为 a => b => c
  2. c的依赖是个数组, 所以需要查找子依赖, 子依赖中d依赖于a, 此时出现了循环依赖, 此时依赖链条为 a=>b=>c=>d=>a。

观察可知, name 为d的depenndencies 为a, a是第一个元素的name。

由此可知, 我们可以把遍历过的name放到一个数组或者set中, 如果遍历到某一个依赖,发现本依赖的dependencies在这个数组中存在, 那么就说明出现了循环依赖。此时应该中断遍历。

代码如下:

// 根据当前组件的dependencies, 查找下一个
const findDep = (deps, dep) => {
    return deps.find(d => d.name === dep.dependencies);
}

/**
 * 检查循环依赖
 * @param {*} deps 
 */
const checkDeps = (deps) => {
    // 遍历过的name
    let visited = [];
    // 是否有循环依赖的标志
    let hasCircle = false;

    const check = (deps, dep) => {
        if (visited.includes(dep.dependencies)) {
            hasCircle = true
            // 当出现循环依赖时,中断调出函数
            return;
        }
        visited.push(dep.name);

        if (typeof dep.dependencies == 'string') {
           // 查找下一个元素, 并递归的检查下一个元素依赖项
            const depItem = findDep(deps, dep);
            if (depItem) {
                check(deps, depItem);
            }

        } else if (dep.dependencies instanceof Array) {
            // 如果是数组, 则遍历子依赖
            const { dependencies } = dep
            dependencies.map((one) => {
                check(dependencies, one);
            })
        }
        return hasCircle;
    }

    for (let i = 0; i < deps.length; i++) {
        const dep = deps[i];
        check(deps, dep);
        if (hasCircle) {
            break;
        }
    }
    return hasCircle;
}

注: 上面之所以定义了一个hasCircle的变量, 是因为在递归中return 要处理好几个地方, 所以用变量来穿透递归省事点。