1719. 重构一棵树的方案数

241 阅读3分钟

「这是我参与2022首次更文挑战的第29,活动详情查看:2022首次更文挑战

1719. 重构一棵树的方案数

给你一个数组 pairs ,其中 pairs[i] = [xi, yi] ,并且满足:

pairs 中没有重复元素 xi < yi 令 ways 为满足下面条件的有根树的方案数:

树所包含的所有节点值都在 pairs 中。 一个数对 [xi, yi] 出现在 pairs 中 当且仅当 xi 是 yi 的祖先或者 yi 是 xi 的祖先。 注意:构造出来的树不一定是二叉树。 两棵树被视为不同的方案当存在至少一个节点在两棵树中有不同的父节点。

请你返回:

如果 ways == 0 ,返回 0 。 如果 ways == 1 ,返回 1 。 如果 ways > 1 ,返回 2 。 一棵 有根树 指的是只有一个根节点的树,所有边都是从根往外的方向。

我们称从根到一个节点路径上的任意一个节点(除去节点本身)都是该节点的 祖先 。根节点没有祖先。

示例 1:

输入:pairs = [[1,2],[2,3]]
输出:1
解释:如上图所示,有且只有一个符合规定的有根树。

示例 2:

输入:pairs = [[1,2],[2,3],[1,3]]
输出:2
解释:有多个符合规定的有根树,其中三个如上图所示。

示例 3:

输入:pairs = [[1,2],[2,3],[2,4],[1,5]]
输出:0
解释:没有符合规定的有根树。  

提示:

1 <= pairs.length <= 105
1 <= xi < yi <= 500
pairs 中的元素互不相同。

代码实现

/**
 * @param {number[][]} pairs
 * @return {number}
 */
var checkWays = function(pairs) {
    // 生成节点元素族谱
    const genealogy = createGenealogy(pairs)
    // 根据元素个数从当前族谱中找出可能是根节点的元素
    let root = checkRoot(genealogy);
    // 如果根据不存在说明不能组成树
    if(root === 0) return 0
    let res = 1;
    // 判断除了根节点以外的节点是否都有对应的祖先节点,当前节与他的祖先节深度相同时说明当前节点与祖先节点互为祖先节点,这样一来就说明会有多个数.
    for (const [node, neg] of genealogy ) {
        if (root === node) continue;
        const curLevel = neg.size;
        // 找比当前节点深度大一点的节点当作祖先节点,后面在对齐判断是否真的是
        let { parentNode, parentLevel } = checkLevelRoot(genealogy,neg)
        // 如果当前节点没有祖先节点就说明不能组成树.
        if(parentNode === 0){
            return 0
        }

        // 1. 与它最近深度的节点完全是当前节点的祖先节点
        // 2. 与它最近深度的节点不是当前节点的祖先节点
        // 3. 与它俩互为祖先节点

        // 判断祖先节点是否完全包含子节点如果不是的话说明不能组成树
        if(!checkLevelRootCorrect(genealogy,neg,parentNode)) return 0

        // 如果当节点深度与祖先节点深度一样说明他俩互为祖先节点
        if (parentLevel === curLevel) {
            res = 2;
        }
    }
    return res
};

// 寻找当前节点最近的祖先节点
var checkLevelRoot = function (genealogy,neg){
    const curLevel = neg.size;
    let parentNode = 0;
    let parentLevel = Number.MAX_SAFE_INTEGER;
    for (const neighbour of neg) {
        if (genealogy.has(neighbour) && genealogy.get(neighbour).size < parentLevel && genealogy.get(neighbour).size >= curLevel) {
            parentNode = neighbour;
            parentLevel = genealogy.get(neighbour).size;
        }
    }
    return {
        parentNode,
        parentLevel
    }
}


// 判断当前节点与它最近的祖先节点的关系

var checkLevelRootCorrect = function(genealogy,neg,parentNode){
    for (const neighbour of neg) {
        if (neighbour === parentNode) {
            continue;
        }
        if (!genealogy.get(parentNode).has(neighbour)) {
             return 0;
        }
    }

    return 1
}

// 是否存在根节点,节点深度为总元素的长度减一
var checkRoot = function(genealogy){
    for (const [node, neg] of genealogy) {
        if (neg.size === genealogy.size - 1) {
            return node;
        }
    }
    return 0
}

// 生成族谱,每个元素当祖先节点的情况
var createGenealogy = function(pairs){
    const genealogy = new Map();
    for (const p of pairs) {
        if (!genealogy.has(p[0])) {
            genealogy.set(p[0], new Set());
        }
        if (!genealogy.has(p[1])) {
            genealogy.set(p[1], new Set());
        }
        genealogy.get(p[0]).add(p[1]);
        genealogy.get(p[1]).add(p[0]);
    }
    return genealogy
}