我有一个数据表,其上有一个遍历树结构,它有很多列,但重要的是如下: | 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花了很多时间 谢谢!
答案 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。您可以迭代它以将其保存到数据库。
需要多长时间?