[英]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.