[英]How can I use binary search to search for the first item in a List greater than a specific value for C# 4.0
[英]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();
给出一些假设:
我现在需要获取此函数的结果并按层次结构对列表进行排序
假设我们有以下团队信息:
|| 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.