Является ли алгоритм сортировки, используемый методом .NET Array.Sort () `стабильным алгоритмом? - PullRequest
58 голосов
/ 29 сентября 2008

Является ли алгоритм сортировки, используемый методом .NET Array.Sort(), алгоритмом stable ?

Ответы [ 5 ]

66 голосов
/ 29 сентября 2008

С MSDN :

Эта реализация выполняет нестабильную сортировку; то есть, если два элемента равны, их порядок может не сохраниться. Напротив, стабильная сортировка сохраняет порядок элементов, которые равны.

Сортировка использует интроспективную сортировку. (Быстрая сортировка в версии 4.0 и более ранних версиях .NET Framework).

Если вам нужна стабильная сортировка, вы можете использовать Enumerable.OrderBy .

64 голосов
/ 29 сентября 2008

Добавление к Ответ Расмуса Фабера

Сортировка в LINQ через Enumerable.OrderBy и Enumerable.ThenBy , является стабильной реализацией сортировки, которую можно использовать в качестве альтернативы Array.Sort . Из Enumerable.OrderBy документации по MSDN :

Этот метод выполняет стабильную сортировку; то есть если ключи двух элементов равны, порядок элементов сохраняется В отличие от нестабильной сортировка не сохраняет порядок элементы, имеющие одинаковый ключ.

Кроме того, любую нестабильную реализацию сортировки, такую ​​как Array.Sort, можно стабилизировать, используя положение элементов в исходной последовательности или массиве в качестве дополнительного ключа, который служит в качестве прерывателя связей. Ниже приведена одна из таких реализаций, как универсальный метод расширения для любого одномерного массива, который превращает Array.Sort в стабильную сортировку:

using System;
using System.Collections.Generic;

public static class ArrayExtensions {

    public static void StableSort<T>(this T[] values, Comparison<T> comparison) {
        var keys = new KeyValuePair<int, T>[values.Length];
        for (var i = 0; i < values.Length; i++)
            keys[i] = new KeyValuePair<int, T>(i, values[i]);
        Array.Sort(keys, values, new StabilizingComparer<T>(comparison));
    }

    private sealed class StabilizingComparer<T> : IComparer<KeyValuePair<int, T>> 
    {
        private readonly Comparison<T> _comparison;

        public StabilizingComparer(Comparison<T> comparison) {
            _comparison = comparison;
        }

        public int Compare(KeyValuePair<int, T> x, 
                           KeyValuePair<int, T> y) {
            var result = _comparison(x.Value, y.Value);
            return result != 0 ? result : x.Key.CompareTo(y.Key);
        }
    }
}

Ниже приведен пример программы с использованием StableSort сверху:

static class Program 
{
    static void Main() 
    {
        var unsorted = new[] {
            new Person { BirthYear = 1948, Name = "Cat Stevens" },
            new Person { BirthYear = 1955, Name = "Kevin Costner" },
            new Person { BirthYear = 1952, Name = "Vladimir Putin" },
            new Person { BirthYear = 1955, Name = "Bill Gates" },
            new Person { BirthYear = 1948, Name = "Kathy Bates" },
            new Person { BirthYear = 1956, Name = "David Copperfield" },
            new Person { BirthYear = 1948, Name = "Jean Reno" },
        };

        Array.ForEach(unsorted, Console.WriteLine);

        Console.WriteLine();
        var unstable = (Person[]) unsorted.Clone();
        Array.Sort(unstable, (x, y) => x.BirthYear.CompareTo(y.BirthYear));
        Array.ForEach(unstable, Console.WriteLine);

        Console.WriteLine();
        var stable = (Person[]) unsorted.Clone();
        stable.StableSort((x, y) => x.BirthYear.CompareTo(y.BirthYear));
        Array.ForEach(stable, Console.WriteLine);
    }
}

sealed class Person 
{
    public int BirthYear { get; set; }
    public string Name { get; set; }

    public override string ToString() {
        return string.Format(
            "{{ BirthYear = {0}, Name = {1} }}", 
            BirthYear, Name);
    }
}

Ниже приведен пример вышеприведенного примера программы (работающей на компьютере с установленными Windows Vista SP1 и .NET Framework 3.5 SP1):

{ BirthYear = 1948, Name = Cat Stevens }
{ BirthYear = 1955, Name = Kevin Costner }
{ BirthYear = 1952, Name = Vladimir Putin }
{ BirthYear = 1955, Name = Bill Gates }
{ BirthYear = 1948, Name = Kathy Bates }
{ BirthYear = 1956, Name = David Copperfield }
{ BirthYear = 1948, Name = Jean Reno }

{ BirthYear = 1948, Name = Jean Reno }
{ BirthYear = 1948, Name = Kathy Bates }
{ BirthYear = 1948, Name = Cat Stevens }
{ BirthYear = 1952, Name = Vladimir Putin }
{ BirthYear = 1955, Name = Bill Gates }
{ BirthYear = 1955, Name = Kevin Costner }
{ BirthYear = 1956, Name = David Copperfield }

{ BirthYear = 1948, Name = Cat Stevens }
{ BirthYear = 1948, Name = Kathy Bates }
{ BirthYear = 1948, Name = Jean Reno }
{ BirthYear = 1952, Name = Vladimir Putin }
{ BirthYear = 1955, Name = Kevin Costner }
{ BirthYear = 1955, Name = Bill Gates }
{ BirthYear = 1956, Name = David Copperfield }
20 голосов
/ 29 сентября 2008

Как уже говорилось в других ответах, Array.Sort не стабилен. Однако методы LINQ OrderBy (и OrderByDescending и т. Д.) стабильны , что может быть очень полезно.

3 голосов
/ 29 сентября 2008

Нет, это не :

Этот метод использует алгоритм быстрой сортировки. Эта реализация выполняет нестабильную сортировку

0 голосов
/ 13 февраля 2013

ОБНОВЛЕНИЕ: Этот код не стабилизирует Array.Sort (убедитесь, что элементы всегда сортируются в одном и том же порядке):

public static class ComparisonExtensions
{
    public static Comparison<T> WithGetHashCode<T>(this Comparison<T> current)
    {
        return (x, y) =>
        {
            var result = current(x, y);
            if (result == 0)
                return x.GetHashCode() - y.GetHashCode();
            return result;
        };
    }
}

Использование:

Comparison<Person> comparison = (x, y) => x.BirthYear.CompareTo(y.BirthYear);
Array.Sort(unstable, comparison.WithGetHashCode());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...