合并Treenodes

有谁知道将以下列方式合并treenodes的算法?

treeA \ child a \node(abc) \ child b \node(xyz) + treeB \ child a \node(qrs) \ child b \node(xyz) \node(pdq) \ child c \node(pdq) = // do merge treeMerged \ child a \node(abc) \node(qrs) \ child b \node(xyz) \node(pdq) \ child c \node(pdq) 

任何帮助将不胜感激。

好吧,一旦我真的花时间考虑它,解决方案结果比我预期的要简单得多。 (我已经发布了以下代码的关键部分)

  private TreeNode DoMerge(TreeNode source, TreeNode target) { if (source == null || target == null) return null; foreach (TreeNode n in source.Nodes) { // see if there is a match in target var match = FindNode(n, target.Nodes); // match paths if (match == null) { // no match was found so add n to the target target.Nodes.Add(n); } else { // a match was found so add the children of match DoMerge(n, match); } } return target; } 

仍然有兴趣知道是否有人有更好的解决方案?

好吧,我承认,当我第一次开始搞乱这个时,我觉得它不会太难,所以我想我会尝试用LINQ来做。 它出来是坚果,但它的确有效。 我确定有更优雅和高效的算法,但在这里!

首先,我在TreeNodeCollection类上有一个ToEnumerable扩展方法:

  public static class TreeNodeCollectionExtensions { public static IEnumerable ToEnumerable(this TreeNodeCollection nodes) { foreach (TreeNode node in nodes) { yield return node; } } } 

然后,我实现了一个自定义比较器:

public class TreeNodeComparer:IEqualityComparer {

 public bool Equals(TreeNode x, TreeNode y) { return x.Text == y.Text; } public int GetHashCode(TreeNode obj) { return obj.Text.GetHashCode(); } 

}

最后,疯狂:

 private TreeView MergeTreeViews(TreeView tv1, TreeView tv2) { var result = new TreeView(); foreach (TreeNode node in tv2.Nodes) { result.Nodes.Add(node.Clone() as TreeNode); } foreach (TreeNode node in tv1.Nodes) { var nodeOnOtherSide = result.Nodes.ToEnumerable() .SingleOrDefault(tr => tr.Text == node.Text); if (nodeOnOtherSide == null) { TreeNode clone = node.Clone() as TreeNode; result.Nodes.Add(clone); } else { var n = node.Nodes.ToEnumerable() .Where(t => !(nodeOnOtherSide.Nodes.ToEnumerable() .Contains(t, new TreeNodeComparer()))); foreach (TreeNode subNode in n) { TreeNode clone = subNode.Clone() as TreeNode; nodeOnOtherSide.Nodes.Add(clone); } } } return result; } 

我编写它的方式是它返回第三个“合并”的treeView。 您可以更改代码,以便将第三个树视图作为参数,以便您可以传入您可能已经拥有的树视图。

再一次,我确定有更好的方法来做到这一点,但它应该工作。

还有一件事我想指出,这只适用于两层深的TreeView。

我提出了这个递归的例子,在C#中完美运行(我自己一直在使用它),请注意,你需要找到一种方法将TreeNode.Nodes转换为数组:

 public static TreeNode[] mergeTrees(TreeNode[] target, TreeNode[] source) { if (source == null || source.Length == 0) { return target; } if (target == null || target.Length == 0) { return source; } bool found; foreach (TreeNode s in source) { found = false; foreach (TreeNode t in target) { if (s.Text.CompareTo(t.Text) == 0) { found = true; TreeNode[] updatedNodes = mergeTrees(Util.treeView2Array(t.Nodes), Util.treeView2Array(s.Nodes)); t.Nodes.Clear(); t.Nodes.AddRange(updatedNodes); break; } } if (!found) { TreeNode[] newNodes = new TreeNode[target.Length + 1]; Array.Copy(target, newNodes, target.Length); newNodes[target.Length] = s; target = newNodes; } } return target; } 

如果使用Node.Name属性设置项的实际路径,则合并有点简单。

首先,像这样创建一个TreeNodeCollection扩展(需要为TreeNodeCollection设置一个区分大小写的Find()方法,这样可以确保唯一路径也可以由Case唯一):

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace TreeViewApp { public static class TreeNodeCollectionExtensions { public static TreeNode[] FindExact(this TreeNodeCollection coll, string keytofind) { TreeNode[] retval; if (String.IsNullOrWhiteSpace(keytofind) || coll == null) { retval = new TreeNode[0]; } else { TreeNode[] badfinds = coll.Find(keytofind, true); List goodfinds = new List(); foreach (TreeNode bad in badfinds) { if (bad.Name == keytofind) goodfinds.Add(bad); } retval = goodfinds.ToArray(); } return retval; } } } 

其次,用Source节点填充树视图……
Thrid,使用Target节点填充树视图…
第四,创建一个空树视图。

然后,它就像这样简单:

  private void btn_Merge_Click(object sender, EventArgs e) { //first merge foreach (TreeNode sourceNode in this.treeview_Source.Nodes) { FindOrAdd(sourceNode, ref this.treeview_Merged); } //second merge foreach (TreeNode targetNode in this.treeview_Target.Nodes) { FindOrAdd(targetNode, ref this.treeview_Merged); } } private void FindOrAdd(TreeNode FindMe, ref TreeView InHere) { TreeNode[] found = InHere.Nodes.FindExact(FindMe.Name); //if the node is not found, add it at the proper location. if (found.Length == 0) { if (FindMe.Parent != null) { TreeNode[] foundParent = InHere.Nodes.FindExact(FindMe.Parent.Name); if (foundParent.Length == 0) InHere.Nodes.Add((TreeNode)FindMe.Clone()); else foundParent[0].Nodes.Add((TreeNode)FindMe.Clone()); } else InHere.Nodes.Add((TreeNode)FindMe.Clone()); } else { //if the item was found, check all children. foreach (TreeNode child in FindMe.Nodes) FindOrAdd(child, ref InHere); } } 

再一次,这个解决方案只有在您拥有唯一路径时才有效…通过扩展,它还可以解决案例级别的唯一性问题。

我在这里发布这篇文章的目的是希望能帮助像我这样的人不得不在没有成功的情况下寻找解决方案而不得不建立自己的解决方案。