在C#中获取Dictionary最高值的关键的好方法

Ard*_* Xi 69 c# linq dictionary max

我正试图获得最大值的关键Dictionary<string, double> results.

这是我到目前为止:

double max = results.Max(kvp => kvp.Value);
return results.Where(kvp => kvp.Value == max).Select(kvp => kvp.Key).First();
Run Code Online (Sandbox Code Playgroud)

但是,由于这似乎有点低效,我想知道是否有更好的方法来做到这一点.

dss*_*539 125

我认为这是使用标准LINQ的最易读的O(n)答案.

var max = results.Aggregate((l, r) => l.Value > r.Value ? l : r).Key;
Run Code Online (Sandbox Code Playgroud)

编辑:CoffeeAddict的解释

Aggregate是众所周知的功能概念Fold的LINQ名称

它遍历集合的每个元素并应用您提供的任何功能.这里,我提供的函数是一个返回更大值的比较函数.循环时,Aggregate记住上次调用函数时的返回结果.它将此作为变量提供给我的比较函数l.变量r是当前选定的元素.

因此,在聚合循环遍及整个集合之后,它将从最后一次调用我的比较函数时返回结果.然后我读了.Key它的成员,因为我知道它是一个字典条目

这是另一种看待它的方法[我不保证这个编译;)]

var l = results[0];
for(int i=1; i<results.Count(); ++i)
{
    var r = results[i];
    if(r.Value > l.Value)
        l = r;        
}
var max = l.Key;
Run Code Online (Sandbox Code Playgroud)

  • +1 dss539:我脑子里有一个痒,就像应该用LINQ做的那样.太好了! (3认同)

Who*_*ich 35

在阅读了各种建议之后,我决定对它们进行基准测试并分享结果.

测试的代码:

// TEST 1

for (int i = 0; i < 999999; i++)
{
  KeyValuePair<GameMove, int> bestMove1 = possibleMoves.First();
  foreach (KeyValuePair<GameMove, int> move in possibleMoves)
  {
    if (move.Value > bestMove1.Value) bestMove1 = move;
  }
}

// TEST 2

for (int i = 0; i < 999999; i++)
{
  KeyValuePair<GameMove, int> bestMove2 = possibleMoves.Aggregate((a, b) => a.Value > b.Value ? a : b);
}

// TEST 3

for (int i = 0; i < 999999; i++)
{
  KeyValuePair<GameMove, int> bestMove3 = (from move in possibleMoves orderby move.Value descending select move).First();
}

// TEST 4

for (int i = 0; i < 999999; i++)
{
  KeyValuePair<GameMove, int> bestMove4 = possibleMoves.OrderByDescending(entry => entry.Value).First();
}
Run Code Online (Sandbox Code Playgroud)

结果:

Average Seconds Test 1 = 2.6
Average Seconds Test 2 = 4.4
Average Seconds Test 3 = 11.2
Average Seconds Test 4 = 11.2
Run Code Online (Sandbox Code Playgroud)

这只是为了了解它们的相对表现.

如果您的优化'foreach'是最快的,但LINQ是紧凑和灵活的.

  • +1花时间来替补它.测试3和4有何不同?他们生成相同的MSIL不是吗? (5认同)

JMa*_*sch 11

也许这对LINQ来说不是很好用.我看到使用LINQ解决方案对字典进行了2次完整扫描(1获取最大值,然后另一次查找kvp以返回字符串.

你可以用一个"老式"的foreach一次性完成它:


KeyValuePair<string, double> max = new KeyValuePair<string, double>(); 
foreach (var kvp in results)
{
  if (kvp.Value > max.Value)
    max = kvp;
}
return max.Key;

  • +1用于减少迭代次数.您可能应该使用double.MinValue初始化max.Value,以确保即使它是负数也能找到最大值. (4认同)
  • 你是对的; OP使用了一个O(2n)算法.使用LINQ查看我对O(n)的回答. (2认同)

Geo*_*aph 9

您可以使用 OrderBy(用于查找最小值)或 OrderByDescending(用于最大值)对字典进行排序,然后获取第一个元素。当您需要找到第二个最大/最小元素时,它也有帮助

通过最大值获取字典键:

double min = results.OrderByDescending(x => x.Value).First().Key;
Run Code Online (Sandbox Code Playgroud)

通过最小值获取字典键:

double min = results.OrderBy(x => x.Value).First().Key;
Run Code Online (Sandbox Code Playgroud)

通过第二个最大值获取字典键:

double min = results.OrderByDescending(x => x.Value).Skip(1).First().Key;
Run Code Online (Sandbox Code Playgroud)

通过第二个最小值获取字典键:

double min = results.OrderBy(x => x.Value).Skip(1).First().Key;
Run Code Online (Sandbox Code Playgroud)


Mar*_*ers 7

这是一种快速的方法.它是O(n),这是最佳的.我看到的唯一问题是它遍历字典两次而不是一次.

你可以做到这一点通过遍历字典一次MaxBymorelinq.

results.MaxBy(kvp => kvp.Value).Key;
Run Code Online (Sandbox Code Playgroud)