合并Treenodes

时间:2009-05-14 23:28:11

标签: c# algorithm

有没有人知道以下列方式合并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)

非常感谢任何帮助。

4 个答案:

答案 0 :(得分:4)

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

   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;

    }

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

答案 1 :(得分:2)

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

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

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

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

公共类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。

答案 2 :(得分:2)

我提出了这个递归示例,在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;
        }

答案 3 :(得分:1)

如果您使用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<TreeNode> goodfinds = new List<TreeNode>();
                foreach (TreeNode bad in badfinds)
                {
                    if (bad.Name == keytofind)
                        goodfinds.Add(bad);
                }
                retval = goodfinds.ToArray();

            }
            return retval;
        }
    }
}

其次,使用源节点填充树视图...
Thrid,用目标节点填充树视图...
第四,创建一个空的树视图。

然后,它就这么简单:

    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);
        }
    }

再一次,此解决方案仅在您拥有唯一路径时才有效...使用扩展名,它还会考虑案例级别的唯一性。

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