如何改进代码以在C#datatable中设置遍历树的深度?

时间:2013-12-18 16:41:06

标签: c# performance tree-traversal

我有一个数据表,其上有一个遍历树结构,它有很多列,但重要的是如下: | REPID | LeaderID |深度|

我想将树的深度设置为: 没有LeaderID(null)的RepID应该是深度0 作为领导者的RepID具有深度0的那个应该是深度1,依此类推

到目前为止,我做了一个递归算法来完成工作,但是因为我迭代了超过400,000行,所以需要花费很多时间。

到目前为止,我的代码如下:

public static DataTable SetTreeLevel(DataTable dtReps)
{
    //Getting the 1st level (reps without a leader)
    var first_level = from r in dtReps.AsEnumerable()
                      where (r.Field<int?>("LeaderID") == null || r.Field<int?>("LeaderID") == 0)
                      select r;

    //Setting the level for the reps obtained
    foreach (var row in first_level)
    {
        row["Depth"] = 1;
    }

    //Setting the next levels
    return setTreeLevelRecursive(dtReps, 2);
}

private static DataTable setTreeLevelRecursive(DataTable dtReps, int depth)
{
    //Getting reps of the last level (depth -1)
    var last_level = from r in dtReps.AsEnumerable()
                     where r.Field<int?>("Depth") == depth - 1
                        select r.Field<int?>("RepID");

    //List to improve performance
    List<int?> last_level_list = last_level.ToList<int?>();

    //Getting the next level reps (leader is on the last level list)
    var actual_level = from r in dtReps.AsEnumerable()
                      where last_level_list.Contains(r.Field<int?>("LeaderID"))
                      select r;

    //List to improve performance
    List<DataRow> actual_level_list = actual_level.ToList<DataRow>();

    foreach (DataRow row in actual_level_list)
    {
        row["Depth"] = depth;
    }

    //Validating if there are reps without depth
    if ((from r in dtReps.AsEnumerable()
         where r.Field<int?>("Depth") == null
         select r).Count() > 0)
    {
        //Asignando siguiente nivel
        setTreeLevelRecursive(dtReps, depth + 1);
    }

    //Regresando resultado
    return dtReps;
}

编辑:使用Servy的优化我编写了以下代码:

var lookup = dtReps.AsEnumerable().ToLookup(x => x.Field<int?>("LeaderID"));

            //First level
            var first_level = from r in dtReps.AsEnumerable()
                              where (r.Field<int?>("LeaderID") == null || r.Field<int?>("LeaderID") == 0)
                              select Tuple.Create(r.Field<int>("RepID"), 1);

            var rows = Traverse(first_level, node => lookup[node.Item1]
                .Select(row => Tuple.Create(row.Field<int>("RepID"), node.Item2 + 1))).ToList();

            foreach (var r in rows)
            {
                (from r_nivel in dtReps.AsEnumerable()
                 where r_nivel.Field<int>("RepID") == r.Item1
                 select r_nivel).FirstOrDefault()["Depth"] = r.Item2;
            }

但是foreach花了很多时间 谢谢!

1 个答案:

答案 0 :(得分:0)

首先,您可以定义Rep对象:

public class Rep
{
    public int RepID {get;set;}
    public int LeaderID {get;set;}
    public int Depth {get;set;}
}

然后使用以下命令从DataTable填充List:

List<Rep> Reps=dtReps.OfType<DataRow>().Select(c=>new Rep(){RepID=Convert.ToInt32(c["RepID"]),LeaderID=Convert.ToInt32(c["LeaderID"])).ToList();

然后通过以下方式为每个领导者的代表创建一个查找表:

Dictionary<int,List<Rep>> LeaderLookup=Reps.GroupBy(c=>c.LeaderID).ToDictionary(c=>c.Key,c=>c.ToList());

现在使用LeaderLookup递归设置Depths。这是非常快的,我使用大约3,000个项目的类似数据,它可以在一秒钟内填充。

为此,您可以定义此功能:

private void RecursivelySetDepthOfChildren(int RepID,int CurrentDepth,Dictionary<int,<List<Rep>> LeaderLookup)
{
    int DepthOfChildren=CurrentDepth+1;
    foreach(Rep child in LeaderLookup[RepID])
    {
         child.Depth=DepthOfChildren;
         RecursivelySetDepthOfChildren(child.RepID,child.Depth,LeaderLookup);
    }
}

然后用:

调用该函数
RecursivelySetDepthOfChildren(0,0,LeaderLookup);
在ToDictionary语句之后

。在该调用完成后,您将拥有一个正确设置深度的List。您可以迭代它以将其保存到数据库。

需要多长时间?