识别IEnumerable是否已排序的方法

时间:2018-08-07 06:18:34

标签: c# list sorting

我有这个扩展方法来检查是否对任何类型的列表进行了排序

public static bool IsSorted<T>(this IEnumerable<T> input)
{
    IEnumerable<T> expectedListASC = input.OrderBy(x => x);
    IEnumerable<T> expectedListDESC = input.OrderByDescending(x => x);
    return expectedListASC.SequenceEqual(input) || expectedListDESC.SequenceEqual(input);
}

但是对于大型列表,这需要时间。有没有更有效的方法来获得相同的结果?

3 个答案:

答案 0 :(得分:5)

这是一种通用方法,应先检测序列是按升序还是降序,然后检查集合的其余部分是否也照应。

尚未经过全面测试,如果决定使用此数据集,则应左右扔数据集并编写单元测试。

public static class CollectionExtensions
{
    public static bool IsOrdered<T>(this IEnumerable<T> collection, IComparer<T> comparer = null)
    {
        comparer = comparer ?? Comparer<T>.Default;

        bool? expectedToIncrease = null;
        using (var enumerator = collection.GetEnumerator())
        {
            bool gotFirst = enumerator.MoveNext();
            if (!gotFirst)
                return true; // empty collection is ordered
            var first = enumerator.Current;
            T second = default(T);

            while (expectedToIncrease is null)
            {
                bool gotSecond = enumerator.MoveNext();
                if (!gotSecond)
                    return true; // only equal elements are ordered
                second = enumerator.Current;

                switch (comparer.Compare(first, second))
                {
                    case int i when i < 0:
                        expectedToIncrease = false;
                        break;

                    case int i when i > 0:
                        expectedToIncrease = true;
                        break;
                }

                if (expectedToIncrease is null)
                    first = second; // prepare for next round
            }

            while (enumerator.MoveNext())
            {
                if (expectedToIncrease.GetValueOrDefault())
                {
                    if (comparer.Compare(second, enumerator.Current) < 0)
                        return false;
                }
                else
                {
                    if (comparer.Compare(second, enumerator.Current) > 0)
                        return false;
                }
            }

            return true;
        }
    }
}

答案 1 :(得分:3)

类似的事情应该起作用:

public static bool IsSorted<T>(IEnumerable<T> input)
{
    if (input is IOrderedEnumerable<T>)
    {
        return true;
    }

    var comparer = Comparer<T>.Default;
    T previous = default(T);
    bool previousSet = false;
    bool? comparisonOrder = null;
    foreach (var value in input)
    {
        if (!previousSet)
        {
            previous = value;
            previousSet = true;
        }
        else
        {
            int comparisonResult = comparer.Compare(previous, value);
            if (comparisonResult != 0)
            {
                if (!comparisonOrder.HasValue)
                {
                    comparisonOrder = comparisonResult > 0;
                }
                else if (comparisonResult > 0 != comparisonOrder)
                {
                    return false;
                }
            }
            previous = value;
        }
    }
    return true;
}

它会在跟踪每个项目的同时浏览每个项目,然后使用默认的比较器(就像.OrderBy()一样)检查它们是否已排序。为了允许沿任一方向检查排序,我存储了第一个非零比较的结果,并将其用作检查点。

正如评论中已经指出的那样,并非所有IEnumerable都是可重复进行的,并且根据提供IEnumerable的实现,重新迭代那些可能是昂贵的。另外,考虑一个IEnumerable返回随机数的情况-每次迭代时,它都会给出不同的值(假设每次的种子都不相同)。

对50,000个项目(5,000个迭代)的排序列表进行测试后发现:

  • Lasse用了2137毫秒来确定它是否已排序。
  • 我的方法花了2348毫秒来确定IEnumerable是否已排序。
  • MineR花了2403毫秒返回结果。

答案 2 :(得分:2)

我提供了以下解决方案,该解决方案与其他解决方案的不同之处仅在于您可以指定比较器,它会告诉您排序集合的顺序。

public static class LinqHelpers
{
    [Flags]
    public enum SortDirections
    {
        NotSorted = 0,
        Ascending = 1,
        Descending = 2,
    }
    public static SortDirections GetSortDirection<T>(this IEnumerable<T> input, IComparer<T> comparer = null)
    {
        comparer = comparer ?? Comparer<T>.Default;

        bool isAsc = true;
        bool isDsc = true;
        bool isFirst = true;
        T last = default(T);
        foreach (var val in input)
        {
            if (isFirst)
            {
                isFirst = false;
            }
            else
            {
                int cmp = comparer.Compare(last, val);
                if (cmp > 0) isAsc = false;
                if (cmp < 0) isDsc = false;
            }
            if (!isAsc && !isDsc) break;
            last = val;
        }
        int result = 0;
        if (isAsc) result |= (int)SortDirections.Ascending;
        if (isDsc) result |= (int)SortDirections.Descending;
        return (SortDirections)result;
    }
}

一些边缘情况:

  • 如果元素为0,则视为在两个方向上都排序。
  • 如果为1个元素,则视为在两个方向上都已排序。
  • 如果所有元素都相同,则视为在两个方向上都已排序。

为什么您的大型数据集速度慢?您正在排序数据,即O(n log n)。这个问题只需要是O(n)。