将n叉树表示为向量并进行遍历
Posted
技术标签:
【中文标题】将n叉树表示为向量并进行遍历【英文标题】:Represent and traverse a n-ary tree as a vector 【发布时间】:2014-04-08 15:44:39 【问题描述】:我已经实现了一个 n-ary 树(更具体地说,是 trie ),我想知道是否有一种方法可以将它表示为向量并对其进行遍历。使用一棵微不足道的二叉树(请参阅this question),但我似乎没有找到用 n 叉树执行此操作的方法。 我的最终目标是将向量表示的树存储到文件中,将其映射并执行快速查找,而无需将其有效地加载到内存中。 使用带有 mmap-ped 指针而不是分配其内部结构的磁盘广告存储 trie 的有效方法也很棒。
谢谢。
【问题讨论】:
【参考方案1】:如果您有一棵树,其中每个节点 n 正好有 k 个子节点,那么您可以继续将节点 n 的子节点放置在数组中的位置 k*n+m 处,其中 m 介于 0 和 k-1 之间。如果每个节点有 k 个或更少的子节点,这也将起作用,但如果许多节点的子节点少于 k 个,它将使用比所需更多的内存。我知道将树存储为节点具有不同数量子节点的数组的唯一另一种方法是存储一个辅助数组,其中对于每个节点,您将偏移量存储到原始数组中以找到该节点的子节点,然后您可以要么还存储每个节点的子节点数,要么简单地查找下一个节点的子节点的偏移量,以确定该节点有多少子节点。
【讨论】:
正是我想出的,只需保留偏移量并将它们用作节点指针以在文件中进行 jmp,但这对我来说似乎是一个缓慢的解决方案,所以我问:)【参考方案2】:任何图形结构都可以表示为节点的平面数组(事实上,这就是计算机内存的工作方式)。您只需要使用索引而不是指针,因为当vector
增长或结构为mmap
'd 时,指针会失效。
这是一个三路树作为平面vector
:
struct Node
Node(int data) : payload(data)
for (int i = 0; i < 3; i++)
child[i] = (size_t)(-1); // invalid
int payload;
size_t child[3];
;
typedef std::vector<Node> Tree;
void add_node(Tree &t, size_t parent, int child_index, int payload)
t.push_back(Node(payload));
t[parent].child[child_index] = t.size() - 1;
您需要单独的逻辑来插入根节点,因为它没有父节点。此外,如果您想要一个可以跨平台使用mmap
'd 文件的版本,您最好使用固定大小的索引类型,而不是特定于平台的int
和size_t
。
遍历代码和指针结构一样,只是
next = node->child[1]
变成
t[t[node_idx].child[1]]
等等。即每个节点的查找都通过Tree
对象。
【讨论】:
在您的示例中,您有恒定数量的子节点(3),如果每个节点都可以有可变数量的子节点(例如 trie)怎么办?如果你应该序列化每个节点,如果中间有 B 和 C 并且它们的大小不是恒定的(因为它们可以有任意数量的子节点),你怎么能从节点 A “跳转”到节点 D? @SimoneMargaritelli 在 trie 中,您有固定数量的子元素、字母表元素,或者如果您规定所有输入都是字节字符串,则只有 256 个。 (对于压缩,您实际上可以将它们视为半字节字符串,存储 16 个子节点并将节点数加倍。)template
-ize 这些结构也很简单,template<typename T, unsigned N> struct Node ...
@larsmans 是的,标准实现是Node *children[256]
,但我使用链表,在需要时增长它,每个节点都是struct uint8_t value; void *data; ll_t children;
...使用的内存更少:)
@SimoneMargaritelli 在正常情况下,是的。也许您应该查看double-array tries 以了解压缩方案。【参考方案3】:
假设你知道树的节点数
您可以将调整向量的大小更改为合适的值
代码->
#include<iostream>
#include<vector>
using namespace std;
vector<int> adj[100000000];
void dfs(int);
int main()
int i,n,root,par;
cin>>n;
//Input line contains n space separated integers.
// The ith integer represents the immediate ancestor of the ith node
// Node having 0 as parent node is the Root Node
i=1;
while(i<=n)
cin>>par;
if(par==0)
root=i;
else
adj[par].push_back(i);
i++;
//traversal of the Tree(Inorder traversal)
dfs(root);
void dfs(int at)
vector<int>::iterator it;
it=adj[at].begin();
cout<<*it<<" ";
while(it!=adj[at].end())
dfs((*it));
it++;
return;
希望这可能会有所帮助
【讨论】:
以上是关于将n叉树表示为向量并进行遍历的主要内容,如果未能解决你的问题,请参考以下文章