按升序遍历树

Traversing a tree in ascending generation order

本文关键字:遍历 升序      更新时间:2023-10-16

首先我想知道这是否是一个定义良好的操作:我们首先访问树的所有叶子(从左到右)。然后我们参观所有叶子的父母(从左到右)。然后是这些父节点的所有父节点,等等,直到访问最后一个未访问的节点。请注意,根不一定是最后访问的节点。如果已经访问了某个父级,我们就忽略它。我想不出这个遍历会失败的反例。

所以假设它是定义明确的。进行这种遍历最有效的算法是什么?为了简化伪代码,我们可以假设它是一个二叉树。先拿到所有的叶子已经很耗时了。但与此同时,当我们获得叶子时,我们可以通过将父母存储在某个地方来提取每一个连续的父母。然后我们访问这些父母列表,每个列表在树中比上一个列表高一代。是这样的吗?不幸的是,我只知道c++,但能用其他语言写出伪代码。

获取二叉树的所有叶子(测试):

template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::obtainLeaves (const std::shared_ptr<Node>& node,
std::list<std::shared_ptr<Node>>& leaves) const {
    if (!node)
        return;
    if (node->isLeaf())
        return leaves.emplace_back(node);
    obtainLeaves(node->left, leaves);
    obtainLeaves(node->right, leaves);
}

尽管叶子的父母可以很容易地通过传递父母列表来存储,但所有连续的父母呢?

template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::obtainLeaves (const std::shared_ptr<Node>& node,
std::list<std::shared_ptr<Node>>& leaves, std::set<std::shared_ptr<Node>>& parents) const {
    if (!node)
        return;
    if (node->isLeaf()) {
        leaves.emplace_back(node);
        parents.emplace(node->parent);
        return;
    }
    obtainLeaves(node->left, leaves, parents);
    obtainLeaves(node->right, leaves, parents);
}

或者,与其一次完成所有任务,不如先拿到树叶。然后通过调用->parent来迭代叶子列表以获得它们的父代。然后和这些父母重复这个过程,以此类推。但对我来说,这似乎非常笨拙和耗时,而且也不能很好地检查重复。

简单正确的解决方案:

建造一棵倒置的树。遍历时,保持一组指向根的反向链接。

创建叶的矢量。在执行此操作时,将它们的指针值记录在哈希映射中。现在走吧。

现在,对于该向量中的每个元素,将其替换为其父元素。并将其添加到哈希图中。如果它已经在散列映射中,则忽略它

重复,直到你的父母都用完了。

这可能不是最佳选择。

我测试了以下内容以正确工作。不过,我不知道它的时间复杂性与Yakk的解决方案相比如何,甚至不知道它在这方面的时间复杂性是多少。我知道每个非叶节点在这里至少被访问两次,这不是一件好事。

template <typename T, typename Comparator>
template <typename F, typename... Args>
inline void BinaryTree<T, Comparator>::traverseUpwards (F f, Args&&... args) const {
    std::list<std::shared_ptr<Node>> leaves;
    const std::size_t N = heightOfTree();
    std::vector<std::list<std::shared_ptr<Node>>> parents(N);
    std::set<std::shared_ptr<Node>> alreadyVisited;
    traverseUpwards(root, leaves, parents, alreadyVisited);
    for (const std::shared_ptr<Node>& node : leaves)
        f(node, std::forward<Args>(args)...);
    for (std::size_t i = 0; i < N; i++) {
        for (const std::shared_ptr<Node>& node : parents[i])
            f(node, std::forward<Args>(args)...);
    }
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::traverseUpwards (const std::shared_ptr<Node>& node,
std::list<std::shared_ptr<Node>>& leaves,
std::vector<std::list<std::shared_ptr<Node>>>& parents,
std::set<std::shared_ptr<Node>>& alreadyVisited) const {
    if (!node)
        return;
    if (node->isLeaf()) {  // e.g. (!node->left && !node->right) for a binary tree.
        leaves.emplace_back(node);
        std::shared_ptr<Node> p = node->parent;
        std::size_t i = 0;
        while (p && alreadyVisited.find(p) == alreadyVisited.end()) {
            parents[i++].emplace_back(p);
            alreadyVisited.emplace(p);
            p = p->parent;
        }
    }
    traverseUpwards(node->left, leaves, parents, alreadyVisited);
    traverseUpwards(node->right, leaves, parents, alreadyVisited);
}
template <typename T, typename Comparator>
inline int BinaryTree<T, Comparator>::heightOfTree (const std::shared_ptr<Node>& node) const {
    return node ? std::max (heightOfTree(node->left), heightOfTree(node->right)) + 1 : -1;
}