简体   繁体   中英

C# sorting arrays in ascending and descending order

I'm having trouble writing a method that returns true if the elements of an array (numbers) are in sorted order, ascending or descending, and false, if they are not in any sorted order. I can return a correct boolean value if the array is ascending but i do not know how to check for descending order as well in the same method. I currently have:

public static bool IsArraySorted(int[] numbers)
{
    for (int i = 1; i < numbers.Length; i++)
    {
        if (numbers[i - 1] > numbers[i])
            return false;
    }

    return true;
}

Anyone able to offer help on how to check for a sorted descending array as well? Cheers!

It should be something like:

public static bool IsArraySorted(int[] numbers)
{
    bool? ascending = null;

    for (int i = 1; i < numbers.Length; i++)
    {
        if (numbers[i - 1] != numbers[i])
        {
            bool ascending2 = numbers[i - 1] < numbers[i];

            if (ascending == null)
            {
                ascending = ascending2;
            }
            else if (ascending.Value != ascending2)
            {
                return false;
            }
        }
    }

    return true;
}

Note the use of the ascending variable to save the "direction" of the array. It is initialized the first time two elements that are different are found.

Note that if you want, you can even return the "direction" of the array:

public static bool IsArraySorted(int[] numbers, out bool isAscending)
{
    isAscending = true;
    bool? ascending = null;

and inside the if (ascending == null)

if (ascending == null)
{
    ascending = ascending2;
    isAscending = ascending2;
}

This is a generic version based on IEnumerable<TSource> :

public static bool IsSorted<TSource>(IEnumerable<TSource> source, out bool isAscending, Comparer<TSource> comparer = null)
{
    isAscending = true;

    if (comparer == null)
    {
        comparer = Comparer<TSource>.Default;
    }

    bool first = true;
    TSource previous = default(TSource);

    bool? ascending = null;

    foreach (TSource current in source)
    {
        if (!first)
        {
            int cmp = comparer.Compare(previous, current);

            if (cmp != 0)
            {
                bool ascending2 = cmp < 0;

                if (ascending == null)
                {
                    ascending = ascending2;
                    isAscending = ascending2;
                }
                else if (ascending.Value != ascending2)
                {
                    return false;
                }
            }
        }

        first = false;
        previous = current;
    }

    return true;
}

Note the use of bool first / TSource previous to handle the i - 1 (and the fact that the for cycle was able to "skip" the first element)

Using Linq -

public static bool IsArraySorted(int[] numbers)
{
    var orderedAsc = numbers.OrderBy(a => a);
    var orderedDes = numbers.OrderByDescending(a => a);

    bool isSorted = numbers.SequenceEqual(orderedAsc) ||
                    numbers.SequenceEqual(orderedDes);
    return isSorted;
}

This uses one loop to test both cases:

public static bool IsSorted<T>(IEnumerable<T> items, Comparer<T> comparer = null)
{
    if (items == null) throw new ArgumentNullException("items");
    if (!items.Skip(1).Any()) return true;  // only one item

    if (comparer == null) comparer = Comparer<T>.Default;
    bool ascendingOrder = true; bool descendingOrder = true;

    T last = items.First();
    foreach (T current in items.Skip(1))
    {
        int diff = comparer.Compare(last, current);
        if (diff > 0)
        {
            ascendingOrder = false;
        }
        if (diff < 0)
        {
            descendingOrder = false;
        }
        last = current;
        if(!ascendingOrder && !descendingOrder) return false;
    }
    return (ascendingOrder || descendingOrder);
}

usage:

int[] ints = { 1, 2, 3, 4, 5, 6 };
bool isOrderedAsc = IsSorted(ints); // true
bool isOrderedDesc = IsSorted(ints.Reverse()); //true

If you make it an extension method you can use it with any type:

bool ordered = new[]{"A", "B", "C"}.IsSorted();
public static boolean checkSortedness(final int[] data) 
{
    for (int i = 1; i < data.length; i++) 
    {
        if (data[i-1] > data[i]) {
            return false;
        }
    }
    return true;
}

Where is my answer? I wrote it about an hour ago:

public enum SortType
{
     unsorted   = 0,
     ascending  = 1,
     descending = 2
}

public static SortType IsArraySorted(int[] numbers)
{
    bool ascSorted = true;
    bool descSorted = true;

    List<int> asc = new List<int>(numbers);            

    asc.Sort();

    for (int i = 0; i < asc.Count; i++)
    {
        if (numbers[i] != asc[i]) ascSorted = false;
        if (numbers[asc.Count - 1 - i] != asc[i]) descSorted = false;
    }

    return ascSorted ? SortType.ascending : (descSorted? SortType.descending : SortType.unsorted);
}

Example:

在此处输入图片说明

It looks more like an academic assignment than a practical question. I guess it does not hurt to return to basics once in a while:

public static bool IsSortedAscOrDesc(int[] arr)
{
    int last = arr.Length - 1;
    if (last < 1) return true;

    bool isSortedAsc = true;
    bool isSortedDesc = true;

    int i = 0;
    while (i < last && (isSortedAsc || isSortedDesc)) 
    {
        isSortedAsc &= (arr[i] <= arr[i + 1]);
        isSortedDesc &= (arr[i] >= arr[i + 1]);
        i++;
    }

    return isSortedAsc || isSortedDesc;
}

An array with 2 (or less) elements is sorted,
{0,0} is sorted asc & desc, {0,1} asc, {1,0} desc, {1,1} asc & desc.
It is possible to use one loop, but it seems faster to separate cases. For an array with more than 2 elements,
if the first element is less than the last element, check: a[i] <= a[i + 1].
Below I use "ai <= (ai = a[i])", compare the old value of ai with the new value of ai, each element is read once.

using System;
class Program
{
    static void Main()
    {
        int i = 512; int[] a = new int[i--]; while (i > 0) a[i] = i--; //a[511] = 1;
        Console.WriteLine(isSorted0(a));
        var w = System.Diagnostics.Stopwatch.StartNew();
        for (i = 1000000; i > 0; i--) isSorted0(a);
        Console.Write(w.ElapsedMilliseconds); Console.Read();
    }

    static bool isSorted0(int[] a)  // 267 ms
    {
        if (a.Length < 3) return true; int j = a.Length - 1;
        return a[0] < a[j] ? incr(a) : a[0] > a[j] ? decr(a) : same(a);
    }
    static bool incr(int[] a)
    {
        int ai = a[0], i = 1, j = a.Length;
        while (i < j && ai <= (ai = a[i])) i++; return i == j;
    }
    static bool decr(int[] a)
    {
        int ai = a[0], i = 1, j = a.Length;
        while (i < j && ai >= (ai = a[i])) i++; return i == j;
    }
    static bool same(int[] a)
    {
        int ai = a[0], i = 1, j = a.Length - 1;
        while (i < j && ai == a[i]) i++; return i == j;
    }

    static bool isSorted1(int[] numbers)  // 912 ms  accepted answer
    {
        bool? ascending = null;
        for (int i = 1; i < numbers.Length; i++)
            if (numbers[i - 1] != numbers[i])
            {
                bool ascending2 = numbers[i - 1] < numbers[i];
                if (ascending == null) ascending = ascending2;
                else if (ascending.Value != ascending2) return false;
            }
        return true;
    }
}

A short version.

    static bool isSorted(int[] a)
    {
        if (a.Length < 3) return true; int i = a.Length - 1, ai = a[i--];
        if (ai > a[0]) while (i >= 0 && ai >= (ai = a[i])) i--;
        else if (ai < a[0]) while (i >= 0 && ai <= (ai = a[i])) i--;
        else while (i >= 0 && ai == a[i]) i--; return i < 0;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM