繁体   English   中英

在C#中,如何排序此项目的哪个项目更大?

[英]In C#, how can I order this list of objects by which item is greater?

我有一个称为Team的简单类,看起来像这样:

public class Team
{
     public Team ParentTeam;
     public string Name;
}

因此,它有一个名称和对另一个团队(其父团队)的引用。

我现在有一个从功能中返回的团队列表

List<Team> list = GetTeamsList();

给出一些假设:

  1. 除一个团队(顶级团队)外,所有团队都有一个家长小组
  2. 列表中返回的每个团队都是同一层次结构的一部分,并且只有一个层次结构(在同一“级别”中没有2个团队)

我现在需要获取此函数的结果并按层次结构对列表进行排序

假设我们有以下团队信息:

|| Team Name || Parent Team Name ||
||-----------||------------------||   
|| Team A    || Team B           ||   
|| Team B    || Team C           ||   
|| Team C    || Team D           ||   
|| Team D    || null             || 

但是GetTeamsList()函数以任何随机顺序返回团队。 例如,它可能会回来列出以下内容:

 var teamA = GetTeamA();
 var teamB = GetTeamB();
 var teamC = GetTeamC();
 var teamD = GetTeamD();

 List<Team> list = new List() { teamD, teamA, teamB, teamC };

我需要在此列表中重新排序,因此它看起来像这样:

 List<Team> list = new List() { teamA, teamB, teamC, teamD };

如何根据团队层次结构将列表重新排序为“正确”的顺序?

到目前为止给出的几种解决方案是正确的 ,并且所有解决方案的团队总数至少都是二次方。 随着团队数量的增加,它们将变得效率低下。

与到目前为止的其他一些解决方案相比,这是一个(1)线性,(2)较短,(3)易于理解的解决方案:

static IEnumerable<Team> SortTeams(IEnumerable<Team> teams)
{
  var lookup = teams.ToDictionary(t => t.ParentTeam ?? new Team());
  var current = teams.Single(t => t.ParentTeam == null);
  do
    yield return current;
  while (lookup.TryGetValue(current, out current));
}

这会按照您想要的顺序产生相反的顺序,因此,如果您希望以其他顺序排列,则在调用的末尾放置一个Reverse:

Console.WriteLine(String.Join(" ", SortTeams(teams).Reverse().Select(t => t.Name)));

之所以有“虚拟”团队,是因为字典不允许键为空。

这是我的建议:

public class Team
    {
        public Team ParentTeam;
        public string Name;

        int Level
        {
            get
            {
                int i = 0;
                Team p = this.ParentTeam;
                while (p != null)
                {
                    i++;
                    p = p.ParentTeam;
                }
                return i;
            }
        }

        static IEnumerable<Team> Sort(IEnumerable<Team> list)
        {
            return list.OrderBy(o => o.Level);
        }
    }

当然,如果有相同级别的团队,则可以使用其他条件对其进行排序。

这应该工作:

static IEnumerable<Team> GetOrdered(IEnumerable<Team> teams)
{
    var set = teams as HashSet<Team> ?? new HashSet<Team>(teams);
    var current = teams.First(t => t.Parent == null);

    while (set.Count > 1)
    {
        yield return current;
        set.Remove(current);
        current = set.First(t => t.Parent == current);
    }

    yield return set.Single();
}

这给了您相反的顺序,因此您应该调用Reverse()以获取您要的顺序。

我们可以找到空团队的优势,定义扩展

    public static IEnumerable<Team> FindAscendants(this IEnumerable<Team> l, Team from)
    {
        Team t = l.FirstOrDefault(x => 
            (x.ParentTeam?.Name ?? "").Equals(from?.Name ?? ""));
        return new List<Team>() { t }.Concat(t != null ?
            l.FindAscendants(t) : Enumerable.Empty<Team>());
    }

并颠倒无效团队上升的顺序

list.FindAscendants(null).Reverse().Skip(1)

编辑

具有yield return的扩展的替代版本

    public static IEnumerable<Team> FindAscendants(this IEnumerable<Team> l, Team from)
    {
        Team t = l.FirstOrDefault(x => 
            (x.ParentTeam?.Name ?? "").Equals(from?.Name ?? ""));
        yield return t;
        if (t != null)
            foreach (Team r in l.FindAscendants(t))
            {
                yield return r;
            }
    }

编辑2

就最有效的解决方案而言, 字典是关键。 正如你可以看到现在,也不再需要颠倒顺序。 因此,优化版本将是

    public static IEnumerable<Team> FindDescendandOptimized(this List<Team> l, Team from)
    {
        int count = l.Count;
        var dic = l.ToDictionary(x => x.ParentTeam?.Name??"");
        Team start = dic[from?.Name??""];
        Team[] res = new Team[count];
        res[count - 1] = start;
        for (int i = count - 2; i >= 0; i--)
        {
            start = dic[start.Name];
            res[i] = start;
        }
        return res;
    }

测试用例和用法

        List<Team> list = new List<Team>();
        Team team = new Team();
        team.Name = "0";
        list.Add(team);
        for (int i = 1; i < 200000; i++)
        {
            team = new Team();
            team.Name = i.ToString();
            team.ParentTeam = list.Last();
            list.Add(team);
        }
        list.Reverse();
        Console.WriteLine("Order List of " + list.Count +" teams");
        Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
        list.Shuffle();
        Console.WriteLine("Shuffled List");
        Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
        DateTime start = DateTime.Now;
        var res = list.FindDescendandOptimized(null);
        list = res.ToList();
        DateTime end = DateTime.Now;
        Console.WriteLine("Reordered List");
        Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
        Console.WriteLine("Benchmark ms: " + (end - start).TotalMilliseconds);
        Console.ReadLine();

测试检查在哪里

    static bool TestOrder(List<Team> list)
    {
        int tot = list.Count;
        for (int i = 0; i < tot; i++)
        {
            if (!list[i].Name.Equals((tot-i-1).ToString()))
            {
                return false;
            }
        }
        return true;
    }

编辑3

最后的考虑也许很明显 绝对最有效的方法是定义一个团队。

public class Team
{
    public string Name;
    public Team ParentTeam;
    public Team ChildTeam;
}

如下所示适当填充

            team.ParentTeam = list.Last();
            list.Last().ChildTeam = team;

以便立即重新排序

        DateTime start = DateTime.Now;
        var res = list.OrderByChild(); //list.FindDescendandOptimized(null);
        list = res.ToList();
        DateTime end = DateTime.Now;
        Console.WriteLine("Reordered List");

直接链接

    public static IEnumerable<Team> OrderByChild(this List<Team> l)
    {
        int count = l.Count;
        Team start = l.First(x => x.ParentTeam == null);
        Team[] res = new Team[count];
        res[count - 1] = start;
        for (int i = count - 2; i >= 0; i--)
        {
            start = start.ChildTeam;
            res[i] = start;
        }
        return res;
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM