當你從數據庫中提取的每個元素,你需要將它添加到它的父。所以,保持一個字典,以幫助找到父母。如果你在孩子的父母之前得到一個孩子,那麼你可以插入一個佔位符,直到你得到真實的東西。
void BuildGroups()
{
foreach(IGroup x /* obtained from database or a collection or wherever */)
AddGroup(x);
}
Dictionary<string,IGroup> _groups = new Dictionary<string,IGroup>;
string _parentGroupName = "PARENT";
void AddGroup(IGroup x)
{
// locate (or create) parent and add incoming group
IGroup parent;
string parentID = x.ParentID ?? _parentGroupName;
if(!groups.TryGetValue(parentID, out parent))
{
parent = new Group(parentID); // SEE NOTE BELOW!
_groups[parentID] = parent;
}
parent.Groups.Add(x);
// locate (or insert) child, and make sure it's up to date
IGroup child;
if(groups.TryGetValue(x.ID, out child))
{
// We must have inserted this ID before, because we found one
// of ITS children first. If there are any other values besides
// ParentID and ID, then copy them from X to child here.
}
else
{
// first time we've seen this child ID -- insert it
_groups[x.ID] = x;
}
}
在_parentGroupName字典元件隨後將是一個虛節點,其孩子們都頂層基團的(即,用NULL作爲PARENTID從數據庫的);從該元素,你可以做一個遞歸遍歷:
VisitGroups(_groups[_parentGroupName], "");
void VisitGroups(string ID, string indent)
{
IGroup x;
if(_groups.TryGetValue(ID, out x))
{
foreach(IGroup y in x.Groups)
{
WriteLine(indent + " {0}", y.ID);
VisitGroups(y.ID, indent + " ");
}
}
}
注:此實現在一個單一的在線運行,通過數據 - 你可以立即添加元素,因爲它們可以從數據庫中檢索,而你只需要一次通過數據。這意味着你可以節省一些時間和一些記憶。但是作爲回報,它要求你能夠分配一個類型爲IGroup()的對象來充當佔位符,以防在其父對象之前檢索到子對象。如果您知道某些關於對象順序的內容,或者如果您以兩遍處理字典,則只能避免該要求,如其他答案中所示。
我使用sentinel值_parentGroupName將頂級節點保留在與其他所有其他集合相同的集合中。如果您願意,您可以輕鬆更改此選項以針對頂級節點使用單獨的集合。
感謝所有的答案。我會在標記答案之前檢查我是否有一個工作解決方案。 :) – Echilon 2009-11-28 18:12:34