泛型排序(C++)
一般讲排序算法的文章,为了方便说明算法本身,待排序元素的类型一般使用整型。还有些文章讲泛型排序,待排序元素可以是任意类型,但对于待排序序列,却一般只支持某一种存储形式,比如定长数组,比如std::vector,但不能同时支持它们。那么我们有没有办法使用泛型技术即支持任意元素类型又支持大多数常用的序列类型进行排序呢?
1. 现有的泛型排序
我们知道STL支持几种泛型排序,像sort,stable_sort,partial_sort,list::sort,但是它们都有一些限制。
- sort和partial_sort只支持支持随机访问迭代器RandomAccessIterator的序列,像vector,但不支持list。
- list::sort只支持list排序。
- stable_sort支持支持双向迭代器BidirectionalIterator的序列,如vector和list等,但不能支持数组(包括指针寻址的)序列,比如下面的数组a和指针p所能访问的序列。
int a[100];
MyClass * p = new MyClass[100];
网上也有一些文章,写如何支持泛型排序,如这篇,但里面所指的泛型是指待排序序列中元素类型的泛型,而不是指待排序序列的泛型。对于待排序序列,它只支持数组存储形式(T a[]),不支持vector和list等存储形式(如:std::vector<T>& a)。
2. 我们的目标
我们希望C++排序算法能支持多种序列类型(当然,序列中的元素类型是任意的),排序函数的原型最好能像下面的声明一样,其中arr为任意类型的序列,size为序列的长度,ascending为升序的标志,降序时设为false。
void Sort(A arr, int size, bool ascending = true);
3. 实现
A arr的声明有点像动态类型语言?是。不过C++不能支持运行时的动态类型推导。我们还是得使用编译期类型推导的template(什么?C++11标准里有自动类型推导?那也许将来我们就不需要这篇文章了)。
我们声明一个排序器的模板基类,如下:
template <typename A>
class Sorter
{
public:
Sorter(void) {}
virtual ~Sorter(void) {}
virtual void Sort(A arr, int size, bool ascending = true) = 0;
private:
Sorter(Sorter& sorter);
Sorter& operator=(const Sorter& sorter);
};
简单说说此类。Sorter的模板参数A就是序列类型,后面我们将看到它是如何被应用的。之所以声明一个基类,是为了实现策略模式。之所以Sort函数被声明为纯虚函数,目的是为了让各种派生的排序算法类重载它。声明Sorter类的复制构造和赋值操作符而不定义它们,是为了避免Sorter子类对象间无意的复制和赋值(参见《Effective C++》条款06)。在实践中,有时我们会根据不同时间和空间的要求,使用不同排序算法,所以,以上代码是为了支持策略模式而存在的,不需要策略模式,不用声明这个基类。这部分不是本文主要要讨论的内容,我们来看看本文的重点。
下面我们以插入排序为例,说明如何实现泛型的排序器。
template <typename T, typename A>
class InsertionSorter : public Sorter<A>
{
public:
InsertionSorter(void) {}
virtual ~InsertionSorter(void) {}
virtual void Sort(A arr, int size, bool ascending = true)
{
for (int j = 1;j < (int)size;j++)
{
T key = arr[j];
int i = j-1;
while (i >= 0 && (ascending?(arr[i] > key):(key > arr[i])))
{
arr[i+1] = arr[i];
i--;
}
arr[i+1] = key;
}
}
}
这个类又增加了一个模板参数T,这个就是序列中元素的类型,有了T,我们的排序就可以支持任意元素类型(只要T支持“大于”操作“>”和“赋值”操作符“=”)。另一方面,从上面的代码可以看到,序列arr用[]寻址,这样所有支持[]操作符的序列类型,比如:数组、指针数组、std::vector、std::deque,MFC的CArray,它们都已重载了操作符[],就都能使用这个排序函数来排序。
举几个例子,来展现不同序列类型如何使用这个进行排序(以下MyClass为自定义的一个类,即编译期替换T的类型,读者可以换成任何其它类型,但无论用什么类型,必须支持操作符“>”和“=”)。
为指针数组排序(MyClass *实际上就是A的编译期类型):
MyClass * pMyClass = new MyClass[100];
// 为pMyClass赋值代码省略
// ...
Sorter<MyClass *> * pMyClassSorterPointer = new InsertionSorter<MyClass, MyClass *>;
pMyClassSorterPointer->Sort(pMyClass, size);
delete pMyClassSorterPointer;
为std::vector序列排序(std::vector<MyClass>&实际上就是A的编译期类型):
std::vector<MyClass> vecMyClass;
// 为vecMyClass赋值代码省略
// ...
Sorter<std::vector<MyClass>&> * pMyClassSorterVector = new InsertionSorter<MyClass, std::vector<MyClass>&>;
pMyClassSorterPointer->Sort(vecMyClass, vecMyClass.size());
delete pMyClassSorterVector;
大家可能会说:std::list没有重载操作符[],无法使用这个泛型排序。是的,要想用这个泛型排序,序列类型必须支持[]。解决办法是,我们继承list类,并让其重载操作符[],代码如下:
template <typename T>
class MyList : public std::list<T>
{
public:
T& operator[](size_type _Pos)
{
int i = 0;
iterator iter = begin();
while (iter != end())
{
if (_Pos == i) break;
iter++;
i++;
}
return *iter;
}
const T& operator[](size_type _Pos) const
{
int i = 0;
const_iterator iter = begin();
while (iter != end())
{
if (_Pos == i) break;
iter++;
i++;
}
&
补充:软件开发 , C++ ,