【问题标题】:Sorting array by frequency of elements in C#在 C# 中按元素的频率对数组进行排序
【发布时间】:2020-01-11 09:43:11
【问题描述】:

这是我目前所拥有的

int[] numbers = { 3,5,4,3,8,8,5,3,2,1,9,5 };
int[] n = new int[12];
int[] k;

foreach (int number in numbers)
{
    n[number]++;
}
Array.Sort(n);
Array.Reverse(n);

foreach (int value in n)
{
    Console.WriteLine(value);
}

我知道我错过了在计算元素频率后对元素的频率进行排序的部分,我无法理解它。我会很感激一些帮助,谢谢!

【问题讨论】:

  • 什么是 k 分数?

标签: c# arrays sorting frequency


【解决方案1】:

你的解决方案有什么问题?

虽然您在代码中正确地保留了名为n 的表中数字的频率,因此我将其称为frequencies,然后您将Sort 这个数组。此操作会破坏您的解决方案,因为每个频率都与其在数组中的位置的相应索引相关联。

例如如果这个数组的一个实例是这个[8,2,1,7,6]。当你在这个数组上调用Sort 方法时,这将导致数组被排序并且数组元素的顺序将是[1,2,7,6,8]。在调用 sort 之前,数组的第一个元素表示在我们的numbers 中已经找到了 8 次数字 0(第一个元素的索引为 0)。排序后,第一个元素是1,这意味着现在数字0出现的频率是1,这显然是错误的。

如果你想保持自己的方式,那么你可以尝试这样的事情:

int[] numbers = {  1,2,2,9,1,2,5,5,5,5,2 };
int[] frequencies = new int[12];
int k = 3;

foreach (int number in numbers)
{
    frequencies[number]++;
}

var mostFrequentNumbers = frequencies.Select((frequency, index) => new 
                                            { 
                                                Number = index,
                                                Frequency = frequency
                                            })
                                      .OrderByDescending(item => item.Frequency)
                                      .Select(item => item.Number)
                                      .Take(k);

foreach (int mostFrequentNumber in mostFrequentNumbers)
{
   Console.WriteLine(mostFrequentNumber);
}

还有其他方法吗?

一个简单的方法是使用像字典这样的数据结构,您可以在其中将数字作为键,将相应的频率作为相应的值。

然后您可以按上述数据结构的降序排列,并保留 k 个最频繁的数字。

int[] numbers = { 1,2,2,9,1,2,5,5,5,5,2 };
int k = 3;

Dictionary<int, int> numberFrequencies = new Dictionary<int, int>();

foreach (int number in numbers)
{
    if(numberFrequencies.ContainsKey(number))
    {
        numberFrequencies[number] += 1;
    }
    else
    {
            numberFrequencies.Add(number, 1);
    }
}


var mostFrequentNumbers = numberFrequencies.OrderByDescending(numberFrequency => numberFrequency.Value)
                                           .Take(k)
                                           .Select(numberFrequency => numberFrequency.Key);


foreach (int mostFrequentNumber in mostFrequentNumbers)
{
    Console.WriteLine(mostFrequentNumber);
}

您也可以仅使用 LINQ 实现相同的目标:

int[] numbers = { 1,2,2,9,1,2,5,5,5,5,2 };
int k = 3;

var mostFrequentNumbers = numbers.GroupBy(number => number)
                                 .ToDictionary(gr => gr.Key, gr => gr.Count())
                                 .OrderByDescending(keyValue => keyValue.Value)
                                 .Take(k)
                                 .Select(numberFrequency => numberFrequency.Key);


foreach (int mostFrequentNumber in mostFrequentNumbers)
{
    Console.WriteLine(mostFrequentNumber);
}

【讨论】:

  • @Ale 不客气。请阅读更新,以便您了解您的方法有哪些不正确之处。
【解决方案2】:

你可以只使用 Linq 扩展:

using System.Linq;
using System.Collections.Generic;

...

private static IEnumerable<int> Solve(int[] numbers, int k) {
    return numbers
        .GroupBy(x => x)
        .OrderByDescending(g => g.Count())
        .Select(g => g.Key)
        .Take(k);
}

然后你可以调用:

var numbers = new []{1,2,2,9,1,2,5,5,5,5,2};
var k = 3;
var result = Solve(numbers, k);
foreach (int n in result)
    Console.WriteLine(n);

【讨论】:

    【解决方案3】:

    非常简洁:

    var frequents = numbers.GroupBy(t => t)
        .Where(grp => grp.Count() > 1)
        .Select(t => t.Key)
        .OrderByDescending(t => t)
        .Take(k)
        .ToList();
    

    【讨论】:

    • Errm...您没有使用过 k 并且我认为 OP 希望包含计数为 1 的数字,不是吗?
    • @RichN 是的,我忘了考虑k,现在添加了。
    猜你喜欢
    • 2020-10-19
    • 2015-10-18
    • 2021-12-03
    • 2019-06-10
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 2021-07-28
    相关资源
    最近更新 更多