1. 程式人生 > >sort對類物件進行排序

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);