如何排序或排序整数列表并选择第N个元素

时间:2018-06-06 07:51:30

标签: c# list linq sorting

我有一个列表,我想从中选择第五个最高元素:

List<int> list = new List<int>();

list.Add(2);
list.Add(18);
list.Add(21);
list.Add(10);
list.Add(20);
list.Add(80);
list.Add(23);
list.Add(81);
list.Add(27);
list.Add(85);

OrderbyDescending不适用于此int列表...

6 个答案:

答案 0 :(得分:2)

int fifth = list.OrderByDescending(x => x).Skip(4).First();

答案 1 :(得分:1)

没有 LINQ 表达式:

int result;
if(list != null && list.Count >= 5)
{
    list.Sort();
    result = list[list.Count - 5];
}
else // define behavior when list is null OR has less than 5 elements

与LINQ表达式相比,它具有更好的性能,尽管我的第二个答案中提到的LINQ solutions是舒适和可靠的。

如果您需要极大的整数列表性能,我建议使用更专业的算法,例如Matthew Watson's answer

注意:调用Sort()方法时会修改列表。如果您不想这样,您必须使用列表的副本,如下所示:

List<int> copy = new List<int>(original);

List<int> copy = original.ToList();

答案 2 :(得分:1)

根据不超过5个元素的列表的严重性,您有2个选项。

如果列表永远不应该超过5,我会把它作为例外:

int fifth;
try
{
    fifth = list.OrderByDescending(x => x).ElementAt(4);
}
catch (ArgumentOutOfRangeException)
{
    //Handle the exception
}

如果您希望它少于5个元素,那么您可以将其保留为默认值并检查它。

int fifth = list.OrderByDescending(x => x).ElementAtOrDefault(4);

if (fifth == 0)
{
    //handle default
}

这仍然是一些有缺陷的因为你可能最终得到第五个元素为0.这可以通过在linq之前将列表类型化为可空的int列表来解决:

var newList = list.Select(i => (int?)i).ToList();
int? fifth = newList.OrderByDescending(x => x).ElementAtOrDefault(4);

if (fifth == null)
{
    //handle default
}

答案 3 :(得分:1)

最简单的方法是对数据进行排序并从前面获取N个项目。这是小数据集的推荐方法 - 任何更复杂的方法都是不值得的。

但是,对于大型数据集,可以更快地执行所谓的Partial Sort

有两种主要方法:使用堆,或使用专门的快速排序。

我链接的文章描述了如何使用堆。我将在下面提出一个部分内容:

public static IList<T> PartialSort<T>(IList<T> data, int k) where T : IComparable<T>
{
    int start = 0;
    int end = data.Count - 1;

    while (end > start)
    {
        var index = partition(data, start, end);
        var rank = index + 1;

        if (rank >= k)
        {
            end = index - 1;
        }
        else if ((index - start) > (end - index))
        {
            quickSort(data, index + 1, end);
            end = index - 1;
        }
        else
        {
            quickSort(data, start, index - 1);
            start = index + 1;
        }
    }

    return data;
}

static int partition<T>(IList<T> lst, int start, int end) where T : IComparable<T>
{
    T x = lst[start];
    int i = start;

    for (int j = start + 1; j <= end; j++)
    {
        if (lst[j].CompareTo(x) < 0) // Or "> 0" to reverse sort order.
        {
            i = i + 1;
            swap(lst, i, j);
        }
    }

    swap(lst, start, i);
    return i;
}

static void swap<T>(IList<T> lst, int p, int q)
{
    T temp = lst[p];
    lst[p] = lst[q];
    lst[q] = temp;
}

static void quickSort<T>(IList<T> lst, int start, int end) where T : IComparable<T>
{
    if (start >= end)
        return;

    int index = partition(lst, start, end);
    quickSort(lst, start, index - 1);
    quickSort(lst, index + 1, end);
}

然后,要访问列表中的第五大元素,您可以执行此操作:

PartialSort(list, 5);
Console.WriteLine(list[4]);

对于大型数据集,部分排序可能明显快于完整排序。

<强>附录

请参阅here了解使用QuickSelect algorithm的其他(可能更好)解决方案。

答案 4 :(得分:0)

LINQ 方法检索第5个最大元素 OR 引发异常 WHEN 列表为null或包含少于5个元素:

int fifth = list?.Count >= 5 ?
    list.OrderByDescending(x => x).Take(5).Last() :
    throw new Exception("list is null OR has not enough elements");


这个元素检索第五大元素 OR null WHEN 列表为null或包含少于5个元素:

int? fifth = list?.Count >= 5 ?
    list.OrderByDescending(x => x).Take(5).Last() :
    default(int?);

if(fifth == null) // define behavior


这个元素检索第5个最大元素 OR 最小元素 WHEN 列表包含少于5个元素:

if(list == null || list.Count <= 0)
    throw new Exception("Unable to retrieve Nth biggest element");

int fifth = list.OrderByDescending(x => x).Take(5).Last();


所有这些解决方案都是可靠的,它们应该从不抛出“意外”的异常。

PS :我在这个答案中使用 .NET 4.7

答案 5 :(得分:0)

Here QuickSelect算法有一个C#实现,用于选择无序IList<>中的第n个元素。

您必须将该页面中包含的所有代码放在static class中,例如:

public static class QuickHelpers
{
    // Put the code here
}

鉴于“库”(实际上是一个很大的代码块),那么你可以:

int resA = list.QuickSelect(2, (x, y) => Comparer<int>.Default.Compare(y, x));
int resB = list.QuickSelect(list.Count - 1 - 2);

现在......通常QuickSelect会选择第n个最低元素。我们以两种方式扭转它:

  • 对于resA,我们基于默认的int比较器创建反向比较器。我们通过反转Compare方法的参数来做到这一点。请注意,索引是基于0的。所以有第0,第1,第2等等。

  • 对于resB,我们使用第0个元素是相反顺序的 list-1 元素的事实。所以我们从后面算。最高元素是有序列表中的list.Count - 1,下一个list.Count - 1 - 1,然后是list.Count - 1 - 2,依此类推

理论上使用Quicksort应该比排序列表然后选择第n个元素更好,因为排序列表平均是O(NlogN)操作,然后选择第n个元素是O(1)操作,所以复合是O(NlogN)操作,而QuickSelect平均是O(N)操作。显然有一个但是。 O表示法没有显示k因子...因此具有小k1的O(k1 * NlogN)可能比具有大k2的O(k2 * N)更好。只有多个现实生活中的基准可以告诉我们(你)什么是更好的,这取决于集合的大小。

关于算法的一个小注释:

  

与quicksort一样,quickselect通常作为就地算法实现,除了选择第k个元素之外,它还对数据进行部分排序。有关与排序的连接的进一步讨论,请参阅选择算法。

因此它修改了原始list的排序。