我在代码库中遇到了这个方法,并想知道Big O是什么。该方法采用一个平面列表并创建一个树,在它去的时候分配父和子值。
private void AddChildren(Group group, IEnumerable<Group> groups)
{
foreach (var g in groups)
{
if (g.ParentId == group.Id)
{
g.Parent = group;
group.Children.Add(g);
AddChildren(g, groups);
}
}
}
已经有一段时间了,因为我在识别直接的n ^ 2(或更糟)方法之外做了大O,但我对它的看法是这样的:
只是扔掉那里的东西所以我可以看看我是否在球场:
n +(x * n)
其中x是if循环中的匹配数。
关于这实际上是什么的任何想法都会很棒,谢谢。
答案 0 :(得分:5)
观察到每个父子关系只调用一次递归函数。在具有 n 节点的树结构中,存在 n - 1 这样的关系,因此AddChildren()
被称为 n 次(包括初次通话)。在每次调用中,由于迭代,方法本身执行的工作(不包括递归调用)是 O(n)。因此, O(n ^ 2)总计。
您可以先将所有组放在一个hashmap中,然后遍历列表一次,在hashmap中查找每个父节点,从而提高 O(n)的复杂性。
答案 1 :(得分:1)
关于代码的运行时顺序已经有很多讨论,所以我不会对此发表评论。 Aasmund Eldhuset的回答对我来说是正确的。
也许你想要这样的东西,O(n)
:
void AssignChildrenAndParent(IEnumerable<Group> groups)
{
var groupById=new Dictionary<GroupId,Group>();
foreach(Group group in groups)
{
groupById.Add(group.Id,group);
}
foreach(Group group in groups)
{
Group parent=groupsById(group.ParentId);
group.Parent=parent;
parent.Children.Add(group);
}
}
这与原始代码略有不同,因为它修复了所有父子关系,而不仅仅是根目录之间的关系。
或者,如果您真的希望代码的行为与原始代码完全相同(如果散列效果很好,则再次为O(n)):
private void AddChildren(Group group, IEnumerable<Group> groups)
{
var children=groups.ToLookup(group=>group.ParentId);
AddChildren(group, groups, lookup);
}
private void AddChildren(Group group, IEnumerable<Group> groups,Lookup<GroupId,Group> lookup)
{
foreach (var g in lookup[group.Id])
{
g.Parent = group;
group.Children.Add(g);
AddChildren(g, groups,lookup);
}
}