【问题标题】:Is the sorting algorithm used by .NET's `Array.Sort()` method a stable algorithm?.NET 的 `Array.Sort()` 方法使用的排序算法是稳定的算法吗?
【发布时间】:2010-09-13 23:13:05
【问题描述】:

.NET 的Array.Sort() 方法使用的排序算法是stable 算法吗?

【问题讨论】:

  • 稳定排序和不稳定排序的区别何时重要?我很好奇。
  • @tuinstoel - 当您想保留元素的初始顺序时。假设您有一个已按某些标准(即按字母顺序)排序的产品列表,如果您使用稳定排序按类别排序,则原始排序标准保留在同一类别的产品组中,产品将现在按类别和字母顺序排序。
  • 这是明显的遗漏。 C++、Golang 和 Python 都提供稳定的就地排序功能。

标签: c# .net


【解决方案1】:

来自MSDN:

此实现执行不稳定的排序;也就是说,如果两个元素相等,则可能不会保留它们的顺序。相比之下,稳定排序会保留相等元素的顺序。

排序使用内省排序。 (.NET 框架 4.0 及更早版本中的快速排序)。

如果需要稳定排序,可以使用Enumerable.OrderBy。

【讨论】:

  • 请注意以使答案实际。排序方式在 4.5 和 4.0 之间发生了变化,从快速排序变成了introspective sort。
  • Here是英文的同一篇文章(内省排序)。
  • OrderBy 并不总是合适的替代品,因为它返回一个副本而不是就地排序。
【解决方案2】:

不,它isn't:

此方法使用快速排序算法。此实现执行不稳定的排序

【讨论】:

    【解决方案3】:

    正如其他答案所述,Array.Sort 不稳定。但是,LINQ OrderBy 方法(和 OrderByDescending 等)是稳定的,非常有用。

    【讨论】:

      【解决方案4】:

      添加到Rasmus Faber's answer…

      在 LINQ 中通过Enumerable.OrderBy 和Enumerable.ThenBy 进行排序是一种稳定的排序实现,可以用作Array.Sort 的替代方案。来自Enumerable.OrderBy documentation over at 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 }
      

      【讨论】:

        【解决方案5】:

        更新:此代码无法稳定 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());
        

        【讨论】:

        • 你显然不明白稳定排序的含义。 Comparison你用没关系,Array.Sort不稳定。
        • 我不这么认为。它由 HashCode 稳定,只要对象具有相同的 HashCode 排序它是不稳定的(它并不意味着引用相等)。所以我写了算法是由 HashCode 稳定的,这意味着只要对象有不同的 HashCode 它是稳定的。
        • 当我在排序前交换 2 个“重复”元素时会发生什么?你认为哈希码会突然改变吗?
        • 如果你复制一个对象,那意味着有相同的HashCode。
        • @leppie:你说得对,我的错。此代码确保元素始终以相同的顺序排序,而不是稳定 Array.Sort()
        猜你喜欢
        • 2010-12-23
        • 2012-05-16
        • 1970-01-01
        • 2013-05-04
        • 1970-01-01
        • 2010-11-28
        • 2016-06-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多