一道笔试题的解法和联想

(注:这里只是想通过排序算法来描述一下抽象的过程,所以大家不要太关注于数组的排序)

作者:方明

原题是这样的,请用C#编写一个可以对任意类型进行冒泡排序算法,我们先看一个整型的冒泡排序来了解一下冒泡排序:

 

整型冒泡排序

 

这个题其实考的重点并不在冒泡排序(当然不懂冒泡排序肯定不行),而是考一种抽象能力,对于冒泡排序来说,任意类型排序他们共同的东西就是冒泡排序的思想,而不同的部分在比较上,所以我们就要想一种方法将变化的部分和不变的部分隔离开,我们首先想到的方法是使用接口,下边是一种解法:

 

接口实现1

 

这个解法可以应对更多的类型排序,只要这些需要排序的类型都实现IArray接口就可以了,但是问题是如果这样,这个排序算法又不支持整型了,我们总不能为了这个排序算法重新封装一下整型吧。我们得继续寻找办法,于是我们想到了系统的一个接口IComparable,这个接口定义通用的比较方法,由值类型或类实现以创建类型特定的比较方法。系统自定义类型几乎都实现了这个接口,所以只要自定义类也实现这个接口就可以利用到这里了,代码如下:

 

接口实现2

 

现在基本符合题意了,但看起来好像还不是好的解决方案,因为每个类型都要实现IComparable仍然是个限制,那如何打破这个限制呢,仔细想想,我们想出另外一个解决方案,就是想办法把比较方式封装起来,然后调用排序函数时传进来不就成了么。

 

接口实现3

 

大家可能觉得这个和上一个方案很象阿,不都要实现一个接口么,其实后一个方案中的最大优点在于你自定义类不用继承任何接口了,我们想一下,为了让某个类型支持这个冒泡排序而去实现一个接口是很牵强的。.net对数组,集合等的排序就是使用这个方法来做的,大家可以研究一下Array类中的静态方法public static void Sort(Array array, IComparer comparer);。那这个是不是最好的解决方案呢,我们本着不撞南墙不回头的原则继续前进,这个接口好像用委托也可以实现吧,那写个版本先:

 

委托实现

 

似乎也没有什么改进,还要写多写一个方法,但是这个委托版本已经基本接近我们的答案了,下面我们使用Lambda表达式来实现我认为的最优的答案(这个答案中需要大家了解Lambda表达式,范型以及Func委托):

 

Lambda表达式实现

 

ps:有网友问,不同类型排序怎么办,我觉得一样是需要把比较的方式和排序算法本身分离开来,我写了个简单例子供参考

 

任意类型排序


作者:Lance
出处:http://www.cnblogs.com/nuaalfm/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2008-08-23 23:18 你听海是不是在笑 阅读(2486) 评论(15) 编辑 收藏

 回复 引用 查看   
#1楼2008-08-23 23:23 | Gray Zhang      
其实,有个叫Comparison的委托就是了……
所以可以这样
void Sort(IComparable array);

void Sort(IEnumerable array, IComparer comparer);

void Sort(IEnumerable array, Comparison comparison);

 回复 引用 查看   
#2楼2008-08-23 23:31 | 江南白衣      
楼上正解
 回复 引用 查看   
#3楼2008-08-23 23:33 | Clingingboy      
委托和Lambda最佳,不过这都需要编程语言的支持.
 回复 引用 查看   
#4楼[楼主]2008-08-23 23:34 | Lance.Liang      
@Gray Zhang
这里并不是单纯为了解决排序问题,而是想通过这个排序来记录一下隔离变化的过程

 回复 引用 查看   
#5楼2008-08-23 23:34 | Gray Zhang      
楼主其实不知不觉中说明了“依赖倒置”的概念呢
 回复 引用 查看   
#6楼2008-08-24 00:35 | Steven Chen      
俄 不管是否框架已经带了这个东西,循序渐进的说明总能给人带来新的思考。

喜欢这篇文章。

 回复 引用 查看   
#7楼2008-08-24 00:38 | Angel Lucifer      
C++ 中使用仿函数进行排序的点子也不错,而且因为内联,比委托的性能高不少。
 回复 引用 查看   
#8楼2008-08-24 00:40 | guojing      
能不能不同类型彼此排序呢?
 回复 引用 查看   
#9楼2008-08-24 00:48 | Angel Lucifer      
同意楼主的观点。
不过另举一例,嘿嘿。
在接口实现时,可以这样实现:

static void BubbleSort<T>(T[] array) where T : IComparable<T>
{
for (int i = 0; i < array.Length - 1; i++)
{
for (int j = array.Length - 1; j > i; j--)
{
if (array[j].CompareTo(array[j - 1]) < 0)
{
T temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
}
}

 回复 引用 查看   
#10楼2008-08-24 10:58 | 水言木      
学习..
 回复 引用 查看   
#11楼[楼主]2008-08-24 11:52 | Lance.Liang      
@Steven Chen

呵,可能这个例子举的不够恰当,如果举个框架中不支持的更实用的例子就好了

@guojing

不同类型的排序的前提也是要提供比较算法,我觉得使用委托是可以解决的

@Angel Lucifer

恩,不错,用范型是更好些

 回复 引用 查看   
#12楼2008-08-24 12:25 | Cat Chen      
你的comparator(也就是Compare())设计其实是不对的,返回bool只能表示两种情况,但实际上偏序关系也必须至少要有3种情况来表示(在数值大小的全序关系中这叫做“大于”、“等于”、“小于”)。

你可以看看楼上提到的IComparable,它的comparator(也就是CompareTo())才是正确的,通过返回一个int来区分这三种状态。当然,继承自IComparable比较麻烦,所以还是写lamda最快,但lamda应该返回int。

 回复 引用 查看   
#13楼2008-08-24 12:31 | Cat Chen      
@Lance.Liang
用泛型确实比较好,只要你多用一点,你就习惯泛型的思维了。我觉得最简单的做法就是多用Linq,你很快就会对泛型和lamda运用自如。

 回复 引用 查看   
#14楼2008-08-25 11:04 | 雅阁布      
不错!!
 回复 引用 查看   
#15楼2008-09-12 19:47 | robi      
@Cat Chen
@Cat Chen
" 你的comparator(也就是Compare())设计其实是不对的,返回bool只能表示两种情况,但实际上偏序关系也必须至少要有3种情况来表示(在数值大小的全序关系中这叫做“大于”、“等于”、“小于”)。"

public bool Compare(IArray IA)
{
return this.Age>((Employee)IA).Age;
}

返回bool并不是表示您说的偏序关系,而是判断this.Age>((Employee)IA).Age是否是正确的。