sort对类对象进行排序

C++编程中常需要对对象进行排序,有可能还要根据对象中的多个成员的值进行排序,C++中提供了sort泛型算法便于利用。需要注意的是,sort排序函数不是稳定的,稳定的排序可以用table_sort。稳定是指函数可保证相等元素的原本相对次序在排序后保持不变。

sort原型

template <class RandomAccessIterator>  
  void sort (RandomAccessIterator first, RandomAccessIterator last);  
  
template <class RandomAccessIterator, class Compare>  
  void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);  

第一个函数有两个参数,分别是随机访问迭代器first和last,它默认使用迭代器引用的operator<进行排序。第二个函数有三个参数,前两个和第一个一样,第三个参数是一个Compare,也就是说它使用comp对迭代器引用的对象进行排序。comp是一个二元函数,它接受前两个参数指定范围内的两个元素,它的返回值可以转换为bool类型,这个函数不改变迭代器引用的对象,而且,它可以是函数指针和函数对象。

下面举一个例子

#include <iostream>  
#include <algorithm>  
#include <vector> 

using namespace std;

int main()
{
	vector<int> ivec = { 1,5,3,6,4 };
	sort(ivec.begin(), ivec.end());
	for (auto &m : ivec)
	cout << m << endl;
	return 0;
}

可以利用一下四种方法来对对象进行排序

重载operator<操作符

struct Test{  
    int val;  
    Test(int x) : val(x) { }  
    bool operator<(const Test &t) const  
    {  
        if(val < t.val)  
            return true;  
        return false;  
    }  
};  
  
bool operator<(const Test &t1, const Test &t2)  
{  
    if(t1.val < t2.val)  
        return true;  
    return false;  
}  
  

在比较时直接利用

sort(ivec.begin(), ivec.end());  

上面写了两种重载方式:成员函数或者非成员函数,不可两个都用,一种即可。

若有两个成员依次比较则可以这样

struct Test {
	int val1;
	int val2;
	Test(int x,int y) : val1(x),val2(y) { }
	bool operator<(const Test &t) const
	{
		if (val1 < t.val1)
			return true;
		else if (val1 == t.val1&&val2 < t.val2)
			return true;
		else
		   return false;
	}
};

使用自定义的比较函数

struct Test {
	int val;
	Test(int x) : val(x) { }
};

bool mycomp(const Test &t1, const Test &t2)
{
	if (t1.val > t2.val)
		return true;
	return false;
}

调用时

sort(ivec.begin(), ivec.end(), mycomp);

对对象的对个成员比较时,对mycomp进行改写

 使用自定义的函数对象

函数对象是定义了函数调用操作符()的类的对象。

struct Test {
	int val;
	Test(int x) : val(x) { }
};

class Mycomp {
public:
	
	bool operator()(const Test &t1, const Test &t2)
	{
		if (t1.val < t2.val)
			return true;
		return false;
	}
};

调用时

sort(ivec.begin(), ivec.end(), Mycomp());

这里定义了一个类Mycomp,它定义了operator(),因此Cmp的对象就是函数对象,它的对象可以当作函数一样使用。在Mycomp的operator()中,有两个Test类类型的参数,它们进行比较得到布尔值。在调用时第三个参数用的是默认构造函数构造的一个Mycomp临时对象。

 对<functional>中的类模板进行派生创建函数对象

在<functional>中定义了多种模板,其中二元函数模板如下:

template <class Arg1, class Arg2, class Result>  
  struct binary_function {  
    typedef Arg1 first_argument_type;  
    typedef Arg2 second_argument_type;  
    typedef Result result_type;  
  };

它只定义了三个参数,分别是二元函数的两个操作数和返回值。在使用中我们需要对上面的类进行派生,添加operator(),然后就可以使用函数对象了

看一下less的实现:

template <class T> struct less : binary_function <T,T,bool> {  
  bool operator() (const T& x, const T& y) const  
    {return x<y;}  
}; 
它是一个带模板的struct,里面仅仅对()运算符进行了重载,实现很简单,但用起来很方便,这就是函数对象的优点所在。stl中还为四则运算等常见运算定义了这样的函数对象,与less相对的还有greater:

template <class T> struct greater : binary_function <T,T,bool> {  
  bool operator() (const T& x, const T& y) const  
    {return x>y;}  
};  

使用时调用、

sort(ivec.begin(), ivec.end(), less<type>());

实现自己的Mycomp

class Mycomp : public binary_function<Test, Test, bool> {  
public:  
    bool operator()(const Test &t1, const Test &t2)  
    {  
        if(t1.val < t2.val)  
            return true;  
        return false;  
    }  
}; 

调用方式

    sort(ivec.begin(), ivec.end(), Mycomp());  

对map按key和value进行排序

map是stl里面的一个模板类,现在我们来看下map的定义:

template < class Key, class T, class Compare = less<Key>,  
           class Allocator = allocator<pair<const Key,T> > > class map;  

它有四个参数,其中我们比较熟悉的有两个: Key 和 Value。第四个是 Allocator,用来定义存储分配模型的,此处我们不作介绍。现在我们重点看下第三个参数: class Compare = less<Key> ,这也是一个class类型的,而且提供了默认值 less<Key>。

    在我们插入<key, value>键值对时,就会按照key的大小顺序进行存储。这也是作为key的类型必须能够进行<运算比较的原因。

例子:map里面存储<string,int>,若按string 从大到小排序

map<string, int, greater<string>> 

自己定义一个按string的长度排序

struct CmpByKeyLength {  
  bool operator()(const string& k1, const string& k2) {  
    return k1.length() < k2.length();  
  }  
}; 

调用

map<string, int, CmpByKeyLength> 

上面都是对Key进行排序,如何对value进行排序?

        首先想到的是利用stl中提供的sort算法实现,这个想法是好的,不幸的是,sort算法有个限制,利用sort算法只能对序列容器进行排序,就是线性的(如vector,list,deque)。map也是一个集合容器,它里面存储的元素是pair,但是它不是线性存储的(map内部本身就是按序存储的(比如红黑树)),所以利用sort不能直接和map结合进行排序。

        虽然不能直接用sort对map进行排序,那么我们可不可以迂回一下,把map中的元素放到序列容器(如vector)中,然后再对这些元素进行排序。元素类型为pair,具体定义如下:
template <class T1, class T2> struct pair  
{  
  typedef T1 first_type;  
  typedef T2 second_type;  
  
  T1 first;  
  T2 second;  
  pair() : first(T1()), second(T2()) {}  
  pair(const T1& x, const T2& y) : first(x), second(y) {}  
  template <class U, class V>  
    pair (const pair<U,V> &p) : first(p.first), second(p.second) { }  
}  
pair也是一个模板类,这样就实现了良好的通用性。它仅有两个数据成员first 和 second,即 key 和 value,而且 <utility>头文件中,还为pair重载了 < 运算符, 具体实现如下:
template<class _T1, class _T2>  
  inline bool  
  operator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)  
  { return __x.first < __y.first  
           || (!(__y.first < __x.first) && __x.second < __y.second); }  

排序方式是先按first小的进行排序,若first相等则按second小进行排序

既然pair已经重载了<符,而且我们不能修改其实现,又不能在外部重复实现重载<符。

那么我们如何实现对pair按value进行比较呢? 第一种:是最原始的方法,写一个比较函数;  第二种:刚才用到了,写一个函数对象。这两种方式实现起来都比较简单。

typedef pair<string, int> PAIR;  
  
bool cmp_by_value(const PAIR& lhs, const PAIR& rhs) {  
  return lhs.second < rhs.second;  
}  
  
struct CmpByValue {  
  bool operator()(const PAIR& lhs, const PAIR& rhs) {  
    return lhs.second < rhs.second;  
  }  
}; 

调用

 vector<PAIR> ivec;
......
sort(ivec.begin(),ivec.end(), CmpByValue());//或者sort(ivec.begin(),ivec.end(), cmp_by_value);



猜你喜欢

转载自blog.csdn.net/alatebloomer/article/details/80191376