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?

时间: 2024-10-29 22:12:33

c#: List.Sort()实现稳固排序(stable sort)的相关文章

排序(sort包)

使用 sort.Interface 来排序 排序是一个在很多程序中广泛使用的操作.sort 包提供了针对任意序列根据任意排序函数原地排序的功能. 这样的设计号称并不常见.在很多语言中,排序算法跟序列数据类型绑定,排序函数跟序列元素类型绑定.但 Go 语言的 sort.Sort 函数对序列和其中元素的布局无任何要求,它使用 sort.Interface 接口来实现. 接口实现 一个原地排序算法需要知道三个信息: 序列长度 比较两个元素的含义 如何交换两个元素 所以 sort.Interface 接

C#中List调用库函数sort进行升序排序

private void button1_Click(object sender, EventArgs e) { List<int> demo2 = new List<int>(); demo2.Add(1); demo2.Add(56); demo2.Add(34); demo2.Add(4); demo2.Add(5); demo2.Add(6); int[] demo3=demo2.ToArray();//z重点是将list转换为arry类型.然后调用arry的sort函数进

希尔排序Shell sort

希尔排序Shell Sort是基于插入排序的一种改进,同样分成两部分, 第一部分,希尔排序介绍 第二部分,如何选取关键字,选取关键字是希尔排序的关键 第一块希尔排序介绍 准备待排数组[6 2 4 1 5 9] 首先需要选取关键字,例如关键是3和1(第一步分成三组,第二步分成一组),那么待排数组分成了以下三个虚拟组: [6 1]一组 [2 5]二组 [4 9]三组 看仔细啊,不是临近的两个数字分组,而是3(分成了三组)的倍数的数字分成了一组, 就是每隔3个数取一个,每隔三个再取一个,这样取出来的数

Java排序方法sort的使用详解

对数组的排序: //对数组排序 public void arraySort(){ int[] arr = {1,4,6,333,8,2}; Arrays.sort(arr);//使用java.util.Arrays对象的sort方法 for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); } } 对集合的排序: //对list升序排序 public void listSort1(){ List<Integer> list =

C++: quick sort(快排序)

到目前为止, 我们已经学习到了插入排序, 冒泡排序, 选择排序(selection). 这些排序算法都是comparision based sorting algorithms(即涉及到元素大小的比较来决定元素的先后顺序). 而且算法的时间复杂度上均为O(n^2).但是comparision based 的排序算法远非这几个算法. 而且可以通过利用其它的一些手段(例如divide and conquer technique, 分治法)实现对基于比较的排序算法的时间复杂度降低到O(nlogn).

算法学习 - STL的p排序函数(sort)使用

排序函数sort() 这个函数是STL自带的,功能很强大~ 这里教下使用方法. sort()有三个参数,第一个是排序的起始位置,第二个是排序的结束位置,第三个是排序的判断函数.函数原型为: sort(<#_RandomAccessIterator __first#>, <#_RandomAccessIterator __last#>, <#_Compare __comp#>) 这个就是原型了~ 使用方法 首先假设我们有一个vector<int> vec;向量

&lt;转&gt;python字典排序 关于sort()、reversed()、sorted()

一.Python的排序 1.reversed() 这个很好理解,reversed英文意思就是:adj. 颠倒的:相反的:(判决等)撤销的 print list(reversed(['dream','a','have','I'])) #['I', 'have', 'a', 'dream'] 2.让人糊涂的sort()与sorted() 在Python 中sorted是内建函数(BIF),而sort()是列表类型的内建函数list.sort(). sorted() sorted(iterable[,

关于C++中vector和set使用sort方法进行排序

C++中vector和set都是非常方便的容器, sort方法是algorithm头文件里的一个标准函数,能进行高效的排序,默认是按元素从小到大排序 将sort方法用到vector和set中能实现多种符合自己需求的排序 首先sort方法可以对静态的数组进行排序 1 #include<iostream> 2 using namespace std; 3 int main(){ 4 int a[10] = { 9, 0, 1, 2, 3, 7, 4, 5, 100, 10 }; 5 sort(a,

160317(二)、按sort排序,sort为空的在后面

按sort排序,sort为空的在后面 select * from 表名 order by (case when sort is null or sort='' then 1 else 0 end),sort select  t.*, t.rowid from hs_tr_goods_attach_rel t order by (case when t.goods_order is null or t.goods_order = '' then 1 else 0 end), t.goods_ord