从一棵树生成团队树的正确递归方式是什么?

时间:2013-09-29 04:05:06

标签: c# recursion

我有一个Person对象,它具有以下属性

public class Person
{
   public string FirstName;
   public string LastName;
   public string TeamName;
   public Person Manager;
   public IEnumerable<Person> DirectReports;
}

所以我可以通过递归循环遍历每个人的直接报告和他们的直接报告等来创建People的层次结构,如下所示:

foreach (Person direct in person.DirectReports)
{
       foreach (Person subDirect in direct.DirectReports)
       {
            etc . . .
       }
}

根据这些数据,我现在正在尝试转换以生成团队的层次结构,所以我有这样的事情:

 public class Team
 {
    public string TeamName;
    public IEnumerable<Team> SubTeams;
    public IEnumerable<Person> PeopleInTeam;
}

请注意,如果某人的directReports可能具有或不具有相同的TeamName,那么Person层次结构中的人员数量不一定与团队层次结构中的级别数相同

例如:

| Person| ParentPerson | TeamName   |  
| Bill  |  ""          | Management |  
| Joe   |  Bill        | Management |  
| Scott |  Bill        | Marketing  |  
| Jim   |  Bill        | Technology |   
| Mark  |  Scott       | Marketing  |
| Bob   |  Joe         | Marketing  |

转换后,我会有一个名为“管理”的团队,里面有2个人。它将在SubTeams数组中有2个项目(一个用于Marketing,一个用于Technology)。每个团队在SubTeams数组中都没有条目

有效利用这种层次结构并将其转换为团队层次结构的最佳方法是什么?我已经列出了下面的代码,但它似乎失败了。现在,我正在遍历每个人和他们的直接报告,并创建一个团队字典,并将每个人一次添加到一个团队,但它似乎很慢。这是一个例子..

 Dictionary<string, Team> teams = new Dictionary<string, Team>();

 foreach (Person direct in person.DirectReports)
 {
       if (teams.ContainsKey(direct.TeamName)
       {
              var team = teams[direct.TeamName];
              team.People.Add(direct);
       }
       else
       {
            var team = new Team(){TeamName = direct.TeamName};
            team.People.Add(direct);
            teams[direct.TeamName] = team;
       }
       foreach (Person subDirect in direct.DirectReports)
       {
            etc . . .
       }
}

2 个答案:

答案 0 :(得分:2)

这是一个可以用作框架的基本解决方案。

这并不理想:特别是Dictionary<string, string>不是一个好的数据结构,因为我们最终还是会循环遍历所有值。构建类似于从部门映射到子部门名称的Dictionary<string, List<string>>之类的东西会更有用。

但它应该为你提供工作的基础。

void Main()
{
    var bill = new Person { FirstName = "Bill", TeamName = "Management" };
    var joe = new Person { FirstName = "Joe", Manager = bill, TeamName = "Management" };
    var scott = new Person { FirstName = "Scott", Manager = bill, TeamName = "Marketing" };
    var jim = new Person { FirstName = "Jim", Manager = bill, TeamName = "Technology" };
    var mark = new Person { FirstName = "Mark", Manager = scott, TeamName = "Marketing" };
    var bob = new Person { FirstName = "Bob", Manager = joe, TeamName = "Marketing" };
    var ted = new Person { FirstName = "Ted", Manager = jim, TeamName = "IT Support" };

    var people = new[] { bill, joe, scott, jim, mark, bob, ted };

    var teamParents = people.Select (p => new { Team = p.TeamName, ParentTeam = p.Manager == null ? null : p.Manager.TeamName });

    // don't let a team be its own parent
    teamParents = teamParents.Where (p => !p.Team.Equals(p.ParentTeam));

    // make sure they're all unique
    teamParents = teamParents.Distinct();

    // put it in a dictionary
    var teamHierarchy = teamParents.ToDictionary (p => p.Team, q => q.ParentTeam);

    foreach (string root in teamHierarchy.Where (h => h.Value == null).Select (h => h.Key))
    {    
        PrintSubteams(teamHierarchy, 0, root);
    }    
}

private void PrintSubteams(Dictionary<string, string> hierarchy, int level, string root)
{
    for (int i = 0; i < level; i++)
    {
        Console.Write("    ");
    }

    Console.WriteLine(root);

    foreach (string child in hierarchy.Where (h => h.Value == root).Select(h => h.Key))
    {
        PrintSubteams(hierarchy, level + 1, child);
    }
}

public class Person
{
    public string FirstName;
    public string LastName;
    public string TeamName;
    public Person Manager;
    public IEnumerable<Person> DirectReports;
}

输出如下:

Management
    Marketing
    Technology
        IT Support

(我添加了IT支持团队,使其更有趣。)

答案 1 :(得分:0)

如果您只有树顶部的人员列表,那么我们可以尝试:

public void ConstructTeams(IEnumerable<Person> topPeople,
    Dictionary<string, Team> teamsByName)
{
    foreach (Person person in topPeople)
    {
        // Add person to their team. Create one if it doesn't exist
        Team matchingTeam;
        if (teamsByName.TryGetValue(person.TeamName, out matchingTeam))
        {
            matchingTeam.PeopleInTeam.Add(person);
        }
        else
        {
            // Create a new team and update its parent team
            matchingTeam = new Team
            {
                TeamName = direct.TeamName
                PeopleInTeam = new List<Person>()
            };
            matchingTeam.PeopleInTeam.Add(person);
            teamsByName.Add(matchingTeam.TeamName, matchingTeam);

            // The manager's team should already exist because we traversing the
            // Person tree/forest from the roots to the leaves
            if (person.Manager != null)
            {
                teamsByName[person.Manager.TeamName].SubTeams.Add(matchingTeam);
            }
        }

        // Recursively fill in the direct reports
        ConstructTeams(person.DirectReports, teamsByName);
    }
}

如果您有所有人的平面列表,这是一个解决方案。

public IEnumerable<Team> ConstructTeams(IEnumerable<Person> allPeople)
{
    var teams = allPeople.GroupBy(person => person.TeamName)
        .Select(grouping => new Team
        {
            TeamName = grouping.Key,
            // I think this works, but IGroupings are weird and I don't
            // have a compiler with me
            PeopleInTeam = grouping.ToList()
        });

    // Set SubTeams
    // Group the teams by parent team. We assumed that
    // no team has two parent teams
    var teamsGroupedByParentTeam = teams
        .GroupBy(team => team.PeopleInTeam.First().TeamName);

    foreach (var groupOfTeams in teamsGroupedByParentTeam)
    {
        var parentTeam = teams.Single(team => team.TeamName == teamsGroupedByParentTeam.TeamName);
        parentTeam.SubTeams = groupOfTeams.ToList();
    }

    return teams;
}