在C+中,有没有一种好方法可以打印出类似JSON的trie结构(仅限迭代解决方案)的扁平名称空间+;11? 我试图在C++中创建一个TIE结构,其中每个节点都保存一个名称和一些数据,并且可以保存对任意数目的子节点的引用。p>
我希望遍历trie,以便以迭代的方式打印到给定节点的“平坦”路径 给定一棵树,其中节点由以下各项定义:在C+中,有没有一种好方法可以打印出类似JSON的trie结构(仅限迭代解决方案)的扁平名称空间+;11? 我试图在C++中创建一个TIE结构,其中每个节点都保存一个名称和一些数据,并且可以保存对任意数目的子节点的引用。p>,c++,c++11,dictionary,traversal,trie,C++,C++11,Dictionary,Traversal,Trie,我希望遍历trie,以便以迭代的方式打印到给定节点的“平坦”路径 给定一棵树,其中节点由以下各项定义: class Node { public: virtual std::string node_name() = 0; }; class TreeNode : Node { public: std::string name; std::map<std::stri
class Node
{
public:
virtual std::string node_name() = 0;
};
class TreeNode : Node
{
public:
std::string name;
std::map<std::string, TreeNode&> children {};
TreeNode(const std::string&name) : name(name) {}
std::string node_name()
{
return name;
}
void add_child(TreeNode& node)
{
children.insert(std::pair<std::string, TreeNode&>
(node.node_name(), node));
}
};
输出应该是(不关心顺序):
我已经实现了递归解决方案(见下文),但我想知道是否有一种迭代实现的方法(因为我希望在应用程序中尽可能避免递归)
以下是递归版本:
void flatten_helper(TreeNode& root,
const std::string& prefix)
{
static const std::string delimeter { "." };
std::string namespace_path(prefix);
if (!prefix.empty())
{
namespace_path.append(delimeter);
}
namespace_path.append(root.node_name());
for (auto& node : root.children)
{
flatten_helper(node.second, namespace_path);
}
// do something with node/complete namespace name
std::cout << namespace_path << std::endl;
}
void flatten(TreeNode& node)
{
std::string empty {""};
return flatten_helper(node, empty);
}
int main(int argc, char** argv)
{
TreeNode root { "root" };
TreeNode a { "a" };
TreeNode b { "b" };
TreeNode c { "c" };
TreeNode d { "d" };
TreeNode e { "e" };
a.add_child(c);
a.add_child(d);
b.add_child(e);
root.add_child(a);
root.add_child(b);
flatten(root);
return 0;
}
void flatten\u助手(TreeNode和root、,
常量std::字符串和前缀)
{
静态常量std::字符串delimeter{“.”};
字符串名称空间路径(前缀);
如果(!prefix.empty())
{
名称空间_path.append(delimeter);
}
名称空间_path.append(root.node_name());
用于(自动节点:root.children(&N)
{
展平辅助对象(node.second,名称空间路径);
}
//对节点/完整命名空间名称执行某些操作
std::cout将递归过程转换为交互过程的诀窍是使“挂起的工作”显式化。在您的例子中,工作单元是TreeNode
和前缀,工作单元保存在std::stack
中(因为递归解决方案是深度优先的)。任何(以前)递归调用必须向堆栈添加工作,当没有更多工作可用时,工作停止
void flatten_iter(TreeNode& root_node)
{
using WorkItem = std::pair<TreeNode&, std::string>;
static const std::string delimeter{ "." };
std::stack<WorkItem> workitems;
workitems.emplace(root_node, "");
while (!workitems.empty()) {
auto [ node, prefix ] = workitems.top();
workitems.pop();
std::string namespace_path(prefix);
if (!prefix.empty())
{
namespace_path.append(delimeter);
}
namespace_path.append(node.node_name());
for (auto& child : node.children)
{
workitems.emplace(child.second, namespace_path);
}
// do something with node/complete namespace name
std::cout << namespace_path << std::endl;
}
}
void flatten\u iter(树节点和根节点)
{
使用WorkItem=std::pair;
静态常量std::字符串delimeter{“.”};
std::堆栈工作项;
workitems.emplace(根节点“”);
而(!workitems.empty()){
auto[node,prefix]=workitems.top();
workitems.pop();
字符串名称空间路径(前缀);
如果(!prefix.empty())
{
名称空间_path.append(delimeter);
}
名称空间_path.append(node.node_name());
用于(自动和子节点:node.children)
{
workitems.emplace(child.second,名称空间_路径);
}
//对节点/完整命名空间名称执行某些操作
避免使用堆栈的一种方法是在树中有父指针
您的迭代器可以向下追踪树,处理底部分支中的所有对等节点,然后使用最后一个节点中的父指针返回1级,移动到该级的下一个对等节点,然后再次向下。最后,这是一个相对最小的算法
当然,对于大型树,每个节点上的父指针的成本比显式堆栈的成本高/多/多,但如果父指针还有其他用途,则值得考虑。这种堆栈表示法也适用于简单的预排序迭代器:开始只是根节点,运算符++
弹出顶部节点并添加子节点,end
是空堆栈。建议您尝试将其作为练习。非常感谢您的解释和练习(我将在下一步尝试)。我有一半的解决方案(使用堆栈w/深度优先迭代),但否定将前缀作为“工作项”应用
void flatten_helper(TreeNode& root,
const std::string& prefix)
{
static const std::string delimeter { "." };
std::string namespace_path(prefix);
if (!prefix.empty())
{
namespace_path.append(delimeter);
}
namespace_path.append(root.node_name());
for (auto& node : root.children)
{
flatten_helper(node.second, namespace_path);
}
// do something with node/complete namespace name
std::cout << namespace_path << std::endl;
}
void flatten(TreeNode& node)
{
std::string empty {""};
return flatten_helper(node, empty);
}
int main(int argc, char** argv)
{
TreeNode root { "root" };
TreeNode a { "a" };
TreeNode b { "b" };
TreeNode c { "c" };
TreeNode d { "d" };
TreeNode e { "e" };
a.add_child(c);
a.add_child(d);
b.add_child(e);
root.add_child(a);
root.add_child(b);
flatten(root);
return 0;
}
void flatten_iter(TreeNode& root_node)
{
using WorkItem = std::pair<TreeNode&, std::string>;
static const std::string delimeter{ "." };
std::stack<WorkItem> workitems;
workitems.emplace(root_node, "");
while (!workitems.empty()) {
auto [ node, prefix ] = workitems.top();
workitems.pop();
std::string namespace_path(prefix);
if (!prefix.empty())
{
namespace_path.append(delimeter);
}
namespace_path.append(node.node_name());
for (auto& child : node.children)
{
workitems.emplace(child.second, namespace_path);
}
// do something with node/complete namespace name
std::cout << namespace_path << std::endl;
}
}