zoukankan      html  css  js  c++  java
  • c#: List.Sort()实现稳固排序(stable sort)

    1. 源起:

    KV 7.0加入列表管理功能,处理排序问题时,对空列表执行按大小、日期、长度排序发现,其中次序会发生改变,令人纳闷。

    没天理呀,不应该啊!List.Sort()方法,它为什么?

    对此问题深入去了解,倒发现了有趣的问题:稳固排序与非稳固排序。

    2、稳固排序与非稳固排序

    微软官方网站找到此段说明:

    Remarks 

    If comparison is provided, the elements of the List<T> are sorted using the method represented by the delegate.

    If comparison is null, an ArgumentNullException is thrown.

    This method uses Array.Sort, which applies the introspective sort as follows:

    • If the partition size is fewer than 16 elements, it uses an insertion sort algorithm

    • If the number of partitions exceeds 2 * LogN, where N is the range of the input array, it uses a Heapsort algorithm.

    • Otherwise, it uses a Quicksort algorithm.

    This implementation performs an unstable sort; that is, if two elements are equal, their order might not be preserved. In contrast, a stable sort preserves the order of elements that are equal.

    On average, this method is an O(n log n) operation, where n is Count; in the worst case it is an O(n ^ 2) operation.

     

    大意是此实现将执行不稳定排序。也就是说,如果两个元素相等,则可能不会保留其顺序。

    我们建Demo做验证,用例如下:

        var list = new List<string>();
        list.AddRange(new string[] { "3", "2", "1", "4" });
    
        list.Sort((x, y) =>
        {
            return 0;
        });
    
        foreach (string s in list)
            Console.WriteLine(s);

    其输出为:

    验证上述结果,推测它是使用了二分法快速反序,把后半部分置前了。

    可八辈的我们宁愿它输入为原始顺序3214,这个合理,但是,可是,它无耻的变了……

     

    3、实际场景

    比如,我们需要对特定的对象进行排序,如下摘取网上例子(链接)稍加修正,以做示例:

        static void Main(string[] args)
        {
            var p1 = new Person() { Name = "Abby", Age = 38 };
            var p4 = new Person() { Name = "Jason", Age = 23 };
            var p2 = new Person() { Name = "Bob", Age = 23 };
            var p3 = new Person() { Name = "Charlie", Age = 23 };
            var p5 = new Person() { Name = "Danielle", Age = 18 };
    
            var list = new List<Person>();
            list.Add(p1);
            list.Add(p2);
            list.Add(p3);
            list.Add(p4);
            list.Add(p5);
            list.Sort();
    
            Console.WriteLine("Unstable List Sort:");
            foreach (Person p in list)
                Console.WriteLine(p);
    
            Console.ReadLine();
        }
        
        class Person : IComparable
        {
            public string Name { get; set; }
            public int Age { get; set; }
            
            public int CompareTo(object obj)
            {
                int result = 1;
                if (obj != null && obj is Person)
                {
                    var person = (Person)obj;
                    result = this.Age.CompareTo(person.Age);;
                }
                return result;
            }
            
            public override string ToString()
            {
                return String.Format("{0} - {1}", this.Name, this.Age);
            }
        }

    其输出为:

    其中间23岁之三项,没有按Jason、Bob、Charlie的次序来,甚至也不是倒序。

    我就是想要它加入顺序不变,怎么办?

    对此问题,搜出一些文章,有建议用LINQ排序因为它是稳固排序,可我们工程所用.net framework版本为2.0,不支持,只好做罢。

    在stackoverflow上,碰到一群难兄难弟,诸般建议阅过,找到可用方法,加索引!

    4、解决方法

    且修改Person类,加入SortIndex属性如下,并修正其比较函数:

        class Person : IComparable
        {
            public string Name { get; set; }
            public int Age { get; set; }
            public int SortIndex { get; set; }
    public int CompareTo(object obj) { int result = 1; if (obj != null && obj is Person) { var person = (Person)obj; result = this.Age.CompareTo(person.Age); if (result == 0) result = this.SortIndex.CompareTo(person.SortIndex); } return result; } ... }

    初始化时,加入其索引:

            var p1 = new Person() { Name = "Abby", Age = 38, SortIndex = 0 };
            var p4 = new Person() { Name = "Jason", Age = 23, SortIndex = 1 };
            var p2 = new Person() { Name = "Bob", Age = 23, SortIndex = 2 };
            var p3 = new Person() { Name = "Charlie", Age = 23, SortIndex = 3 };
            var p5 = new Person() { Name = "Danielle", Age = 18, SortIndex = 4 };

    输出顺序:

    保留初始顺序,解决问题。

     

    后记:

    之前未曾留意,此问题发现倒是很有意思,度娘不给力,解决问题还是要google,在stackoverflow上找到有同困扰之人,得其可参考方案。

    此场景,通常用于自定义数据结构比较,如KV项目之空白列表,当其可比较项相等时,应该保留其原始顺序,可是被改变了,导致寻求解决问题的方法。

    但在官方方案已固定不能改变之情况下,做个曲线救国,加另一索引以固定次序,虽然看来费些点工夫,但是能解决问题,不失为好的可用方法。

    参考文档:

    List(T).Sort Method (Comparison(T)) (System.Collections.Generic)

    C# Stable Sort : C# 411

    Why does List<T>.Sort method reorder equal IComparable<T> elements?

  • 相关阅读:
    【JZOJ4928】【NOIP2017提高组模拟12.18】A
    【JZOJ4922】【NOIP2017提高组模拟12.17】环
    【JZOJ4923】【NOIP2017提高组模拟12.17】巧克力狂欢
    【JZOJ4924】【NOIP2017提高组模拟12.17】向再见说再见
    【JZOJ4919】【NOIP2017提高组模拟12.10】神炎皇
    【JZOJ4920】【NOIP2017提高组模拟12.10】降雷皇
    【JZOJ4921】【NOIP2017提高组模拟12.10】幻魔皇
    【罗宾欺诈者】回环符文——回文树(回文自动机)
    【怪物】KMP畸形变种——扩展KMP
    【51NOD1304】字符串的相似度
  • 原文地址:https://www.cnblogs.com/crwy/p/7784057.html
Copyright © 2011-2022 走看看