知道:
树是相似的,即它们共享一个共同的连接子图,包括根(不一定包括叶子 - 但可能)。
是否存在允许有效存储该信息的任何数据结构?创建结构后,我只对阅读感兴趣。
它不一定是.NET的解决方案,我可以从头开始编码,我只需要这个想法:D当然,如果.NET中有一些鲜为人知的结构那种实现了这一点,我很高兴知道。
我有一种感觉,这个共享内存的东西可能与不可变结构有关,根据定义它们应该共享内存...
不幸的是,我的树不是二元搜索树。他们可以有任何数量的孩子。
至于阅读,这很简单。我总是从
连接的子图包括两个树中相同(可能)的根 始终包含根并向下跨越。在某些情况下,甚至可以到达树叶。查看示例(黄色部分是连接的子图,包括根):
根据这些规则,从数学上讲,所有的树都是相似的 - 连接的子图是空的,或者它只包含根,或者是归纳的 - 它包含根及其子... < / p>
答案 0 :(得分:3)
您可以按不同的&#34;所有者&#34;对您的树节点的子节目进行分组。添加节点时,指定所有者(或使用默认值&#34;共享&#34;所有者)。遍历树时,还指定了所有者。这是一个草图代码:
class TreeNode {
protected static readonly object SharedOwner = new object();
}
class TreeNode<T> : TreeNode {
private readonly T _data;
private readonly Dictionary<object, List<TreeNode<T>>> _children;
public TreeNode(T data) {
this._data = data;
_children = new Dictionary<object, List<TreeNode<T>>>();
}
public TreeNode<T> AddChild(T data, object owner = null) {
if (owner == null)
owner = SharedOwner;
if (!_children.ContainsKey(owner))
_children.Add(owner, new List<TreeNode<T>>());
var added = new TreeNode<T>(data);
_children[owner].Add(added);
return added;
}
public void Traverse(Action<T> visitor, object owner = null) {
TraverseRecursive(this, visitor, owner);
}
private void TraverseRecursive(TreeNode<T> node, Action<T> visitor, object owner = null) {
visitor(node._data);
// first traverse "shared" owner's nodes
if (node._children.ContainsKey(SharedOwner)) {
foreach (var sharedNode in node._children[SharedOwner]) {
TraverseRecursive(sharedNode, visitor, owner);
}
}
// then real owner's nodes
if (owner != null && owner != SharedOwner && node._children.ContainsKey(owner)) {
foreach (var localNode in node._children[owner]) {
TraverseRecursive(localNode, visitor, owner);
}
}
}
}
示例用法:
class Program {
static void Main(string[] args) {
// this is shared part
var shared = new TreeNode<string>("1");
var leaf1 = shared.AddChild("1.1").AddChild("1.1.1");
var leaf2 = shared.AddChild("1.2").AddChild("1.2.1");
var firstOwner = new object();
var secondOwner = new object();
// here we branch first time
leaf1.AddChild("1.1.1.1", firstOwner);
leaf2.AddChild("1.2.1.1", firstOwner);
// and here another branch
leaf1.AddChild("1.1.1.2", secondOwner);
leaf2.AddChild("1.2.1.2", secondOwner);
shared.Traverse(Console.WriteLine, firstOwner);
shared.Traverse(Console.WriteLine, secondOwner);
Console.ReadKey();
}
}
答案 1 :(得分:1)
“重用”具有不同叶子的树的一部分的问题是,您需要提供有关如何将公共部分的叶子映射到不同图形的其他信息。由于您的搜索可以在公共部分中的任何节点中结束,这意味着您需要将此公共子树中的每个节点映射到每个图形内的“实际”节点。
例如,这两个“相似”树A和B共享子树的公共部分(节点1
,3
,6
,7
,{{ 1}}):
要重复使用“公共部分”,您可以执行以下操作:
这是否可以节省空间?好吧,如果知道8
和A
意味着您可以直接“计算”3
而无需查找,那么在此特定示例中,您不需要映射“内部”任何图表的公共节点A3
和3
,节省了一点空间。
换句话说,如果这些公共子树不仅共享其结构,而且还共享其内容,那么您只需将出口节点(叶子)映射到单独的图节点。
(适用更新)强>
为了完整起见,我添加了一个@Evk's implementation图表,它将查找表存储在实际节点中。在空间方面,它不应该有所不同,但由于您在该答案中有一个有效的例子,因此可视化它可能是有用的:
既然你知道你正在处理的实际数据的细节,你可能会在这里和那里挤出一点空间,但我的建议仍然是:
答案 2 :(得分:0)
如果我理解你的问题,解决方案的一部分就是让几棵树共享子树的根,以及叶子中的信息告诉假期所属的树。排列此信息的方式取决于您需要执行的查询类型。
根据新的解释,我理解你需要代表最大树并使用“停止列表”增强节点,该“停止列表”指示部分树中哪个停止在此节点,即不共享更多后代。
停止列表的相应数据结构再次取决于访问模式。
这种重复很可能比简单的树木更紧凑。
答案 3 :(得分:-3)
您是否尝试过AVL Trees(自动平衡二叉树)?如果没有,这种数据结构在这种情况下是有效的。