[英]Algorithm to remove all objects of a tree from a list
我有一个需要从列表中删除树的所有对象的问题。
我有一个List<String> Tags
,其中包含整个系统中符合特定条件的标记(通常以某些搜索字符串开头)。 我也有一个根Device
对象。 Device
类的描述如下:
public class Device
{
public int ID;
public String Tag;
public EntityCollection<Device> ChildDevices;
}
我所做的尝试是使用广度优先搜索,并在访问每个节点时从列表中删除标签,然后返回剩余的内容:
private List<String> RemoveInvalidTags(Device root, List<String> tags)
{
var queue = new Queue<Device>();
queue.Enqueue(root);
while (queue.Count > 0)
{
var device = queue.Dequeue();
//load all the child devices of this device from DB
var childDevices = device.ChildDevices.ToList();
foreach (var hierarchyItem in childDevices)
queue.Enqueue(hierarchyItem.ChildDevice);
tags.Remove(device.Tag);
}
return tags;
}
目前,我正在访问2000多个设备节点,并从大约1400个标签的列表中删除(由于搜索字符串而减少)。 这大约需要4秒钟,太长了。
我曾尝试将标签列表更改为哈希集,但它带来的速度改进可忽略不计。
关于算法/更改的任何想法,我可以用来使其更快?
我猜你的树很“胖”。 也就是说,您的每个节点都有许多子级,但是您没有很多层。 如果是这种情况,请尝试“ 深度优先搜索” 。 您应该快速到达最低点,然后能够开始删除节点。 您仍然必须访问所有节点,但是不必像在BFS中那样存储尽可能多的中间数据。
您绝对应该使用某种哈希表(对不起,不熟悉c#的细节)来访问标签。
我对从数据库加载子设备的过程感到好奇。 由于您要遍历整个树,因此您可能能够将大小更大的块加载到内存中。 广度优先搜索可能会在开始从队列中删除节点之前(如果树很宽)将大多数树加载到内存中。
您可以使用Stopwatch
来了解瓶颈,如果您问我
var childDevices = device.ChildDevices.ToList();
foreach (var hierarchyItem in childDevices)
queue.Enqueue(hierarchyItem.ChildDevice);
那就是你的瓶颈。
看一下C#中的Tree实现 ,希望您已经了解Tree Traversals 。
你为什么不尝试这个?
foreach (var hierarchyItem in device.ChildDevices)
queue.Enqueue(hierarchyItem.ChildDevice);
您不需要将device.ChildDevices转换为list,因为它已经可以枚举。 当您将其转换为列表时,它会很渴望,这很枚举,会很懒。
试试看
最好对代码进行分析或配置,以找出大部分时间在哪里。 关于“向数据库加载查询”( 即 childDevices = device.ChildDevices.ToList();
)花费时间的早期注释和答案可能是正确的,但似乎有可能是
tags.Remove(device.Tag);
那是浪费时间。 .Remove()对每个排队的项目完成。 删除需要O(n)
时间:“此方法执行线性搜索;因此,此方法是O(n)运算,其中n为Count。” [MSDN]
也就是说,假设您排队了m
设备项,其中许多设备项具有.Tag不在n
个条目的tags
列表中。 当查找不在列表中的.Tag时,.Remove会触摸tags
每个元素; 平均而言,它会查看n/2
个条目以找到列表中的.Tag,因此总工作量为O(m*n)
。 相比之下,以下方法的工作量为O(m + n)
,通常会小数百倍。
要回避问题:
tags
列表 tags
列表的每个元素T,如果T在D输出T中,则抑制T
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.