论坛中经常有人问选择法、冒泡法排序算法用C# 怎么实现,正好我手头上有这方面的代码,干脆拿出来跟大家分享一下。其实代码已经写了很久了,只是平时工作比较忙,没时间整理。现在失业了,每天呆在家里实在无聊,就抽空把这部分代码整理出来。好了,废话少说,进入正题吧。
排序算法,首先应该考虑的是通用性,不管是数组,还是List<T> 都应该支持,那肯定要用到泛型了,那么是不是应该用形如下面的实现呢?
- public class Sorter
- {
- public void Sort<T>(T[] array)
- {
- //数组的排序算法
- }
- public void Sort<T>(List<T> list)
- {
- //List<T>的排序算法
- }
- }
这是C++ 的风格,不是C# 的风格。那么C# 的风格是什么?当然是接口了。那么数组和List<T> 有共同的接口吗?当然有了,那就是IList<T> 接口。好了,类型的形式已经确定了。
好了,那下一步泛型应该采用什么形式呢?
形式(1):
- public class Sorter<T>
- {
- public void Sort(IList<T> sort)
- {
- //排序过程
- }
- }
形式(2):
- public class Sorter
- {
- public void Sort<T>(IList<T> sort)
- {
- //排序过程
- }
- }
咋一看,当然是选择形式(1 )了,尤其是当Sorter 类有多个Sort 方法时候。其实不然,因为Sorter 类只有函数成员,没有数据成员,要排序的数据作为参数传递结Sort 方法。那你可能会问,编码的时候要在每一个Sort 方法名称后加上<T>, 不是很麻烦吗?不错,采用形式(2 )编码的时候的确麻烦,但是我们的排序算法是要做成类库的,是要供大家反复调用的。那么你在调用的时候是愿意使用下面那种形式呢?
形式(1)的调用:
-
- int [] array = { 3, 7, 2, 1 };
- Sorter< int >.Sort(array);
形式(2)的调用:
- int [] array = { 3, 7, 2, 1 };
- Sorter.Sort(array); //注意这里可以不用加数据类型,运行的时候会自动识别参数的类型
毫无疑问,你肯定会选择形式(2)了。好了,泛型的形式也确定了。
下一步该考虑Sorter 类的设计了,是应该把Sorter 设计成工具类吗?
-
- public class Sorter
- {
- public static void BubbleSort<T>(IList<T> list)
- {
- //冒泡法排序
- }
- public static void SelectionSort<T>(IList<T> list)
- {
- //选择排序
- }
- //
- //其他排序算法
- //
- }
这也太不符合面向对象的概念了,面向对象的精髓是什么?继承、封装与多态。没错,这里我们继承和多态,将Sorter 设计成虚拟基类,而具体的排序过程留到子类中实现。整个类库的形式应该如下:
- /// <summary>
- /// 虚拟基类
- /// </summary>
- public abstract class Sorter
- {
- public abstract void Sort<T>(IList<T> list);
- }
- /// <summary>
- /// 冒泡法
- /// </summary>
- public class BubbleSorter : Sorter
- {
- public override void Sort<T>(IList<T> list)
- {
- //排序过程
- }
- }
- /// <summary>
- /// 选择法
- /// </summary>
- public class SelectionSort : Sorter
- {
- public override void Sort<T>(IList<T> list)
- {
- //排序过程
- }
- }
- //
- //其他排序方法
- //
好了,既然是排序,当然要涉及到比较的问题了,C#中提供两种比较的方法:IComparable<T>和IComparer<T>接口,IComparable接口有唯一的一个方法:
- int CompareTo(T other);
IComparer<T> 接口也只有一个方法:
- int Compare(T x, T y);
两者参数的个数不一样,怎么把他们统一起来?当然是用委托啦。先定义一个委托:
- public delegate int CompareDelegate<T>(T left, T right);
然后稍微改变一下Sorter 类的代码:
- /// <summary>
- /// 虚拟基类
- /// </summary>
- public abstract class Sorter
- {
- public abstract void Sort<T>(IList<T> list, CompareDelegate<T> compare); //抽象排序算法,由子类实现
- public void Sort<T>(IList<T> list) where T : IComparable<T>
- {
- CompareDelegate<T> compare = delegate (T left, T right)
- {
- return left.CompareTo(right);
- };
- Sort(list, compare);
- }
- public void Sort<T>(IList<T> list, IComparer<T> comparer)
- {
- Sort(list, comparer.Compare);
- }
- }
最后,关于升序排列和降序排列的问题。你可能会认为,这还不简单,只要稍微改变IComparable<T>类型的ComparetTo方法就可以了。那么对于int、double这样的内置数据类型或者第三方提供的类型,你无法改变其源代码,那怎么处理?当然办法还是有的,自定义一个IComparer<T>类。那要对同一类型,要求既可以进行升序排列又可以进行降序排列怎么办?当然办法还是有的,定义两个IComparer<T>类,分别用于升序和降序排列。但是这些办法都不够灵活。这里,我们的Sorter基类将升序排列和降序排列考虑进来,得到了Sorter类的最终版本:
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace CYB.DataStruct.Sorting
- {
- /// <summary>
- /// 用于定义升序和降序的枚举
- /// </summary>
- public enum Order
- {
- ASC,
- DESC,
- }
- public delegate int CompareDelegate<T>(T left, T right);
- /// <summary>
- /// 作者 : cyb
- /// 发表时间 : 2008-9-8
- /// qq : 13101908
- /// e-mail : hustcyb@gmail.com
- /// </summary>
- public abstract class Sorter
- {
- public virtual void Sort<T>(IList<T> list, CompareDelegate<T> compare)
- {
- if (list == null )
- {
- throw new ArgumentNullException( "Argument for IList is null" );
- }
- if (compare == null )
- {
- throw new ArgumentNullException( "Argument for CompareDelegate is null" );
- }
- }
- public void Sort<T>(IList<T> list, Order order) where T : IComparable<T>
- {
- CompareDelegate<T> compare;
- if (order == Order.ASC)
- {
- compare = delegate (T first, T second)
- {
- return first.CompareTo(second);
- };
- }
- else
- {
- compare = delegate (T first, T second)
- {
- return -first.CompareTo(second);
- };
- }
- Sort(list, compare);
- }
- public void Sort<T>(IList<T> list) where T : IComparable<T>
- {
- this .Sort(list, Order.ASC);
- }
- public void Sort<T>(IList<T> list, IComparer<T> comparer, Order order)
- {
- CompareDelegate<T> compare;
- if (order == Order.ASC)
- {
- compare = delegate (T first, T second)
- {
- return comparer.Compare(first, second);
- };
- }
- else
- {
- compare = delegate (T first, T second)
- {
- return -comparer.Compare(first, second);
- };
- }
- Sort(list, compare);
- }
- public void Sort<T>(IList<T> list, IComparer<T> comparer)
- {
- this .Sort(list, comparer, Order.ASC);
- }
- /// <summary>
- /// 交换集合中的两个元素,子类中会用到
- /// </summary>
- public static void SwapListItem<T>(IList<T> list, int firstIndex, int secondIndex)
- {
- T temp = list[firstIndex];
- list[firstIndex] = list[secondIndex];
- list[secondIndex] = temp;
- }
- }
- }
这里要注意一点,IComparable<T>和IComparer<T>的CompareTo方法是假定为升序排列编写的,否则Order.ASC和Order.DESC就没有意义了。
磨刀不误砍柴功,基类设计好了,子类的实现就比较简单了。子类的实现即排序的具体细节将会在近期整理出来。