按升序遍历树
Traversing a tree in ascending generation order
首先我想知道这是否是一个定义良好的操作:我们首先访问树的所有叶子(从左到右)。然后我们参观所有叶子的父母(从左到右)。然后是这些父节点的所有父节点,等等,直到访问最后一个未访问的节点。请注意,根不一定是最后访问的节点。如果已经访问了某个父级,我们就忽略它。我想不出这个遍历会失败的反例。
所以假设它是定义明确的。进行这种遍历最有效的算法是什么?为了简化伪代码,我们可以假设它是一个二叉树。先拿到所有的叶子已经很耗时了。但与此同时,当我们获得叶子时,我们可以通过将父母存储在某个地方来提取每一个连续的父母。然后我们访问这些父母列表,每个列表在树中比上一个列表高一代。是这样的吗?不幸的是,我只知道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;
}
- 有什么方法可以遍历结构吗
- 在循环中按顺序遍历成员变量
- 遍历模板参数
- 在遍历处理程序的向量时注册和注销处理程序
- C++RapidXml-使用first_node()遍历以修改XML文件中节点的值
- 遍历并行数组以确定C++中的最大数字
- 遍历顺序由 std::文件系统directory_iterator给出
- 遍历链表时的无限循环
- 遍历unordered_map向量
- 从预序遍历构造 bst 的 c++ 和 python 解决方案之间的区别
- C++声明双链表,使用两个 for 循环双向遍历列表并打印
- 如何正确地推回然后遍历堆中对象的向量?
- 遍历二维数组的所有子数组
- 如何在可变参数模板函数中遍历可变参数元组?
- 避免在遍历 IShellItemArray 时出现代码重复
- 从特定键开始遍历地图
- 关于链表遍历和调试的困惑
- 将树节点添加到向量向量中的 n 元树遍历的平均和最坏情况时间复杂度是多少?
- 使用相同的 for 循环按升序或降序遍历地图
- 按升序遍历树