1. 程式人生 > >c++中sort等演算法中比較操作的規則

c++中sort等演算法中比較操作的規則

對於c++中,標準庫中提供的演算法很多,這些演算法(如sort等)都會有自己預設的對關鍵字的處理規則,這些都是適用於關鍵字符合要求的情況,而如果我們待處理的關鍵字是自己自定義的類的時候,這個時候就需要我們自己定義其中的處理規則,下面以sort為例:

對於sort這種排序的演算法,c++中預設的比較操作是<運算子,用來處理非降序的排序要求。這些演算法中提供的操作必須在關鍵字型別上定義一個嚴格弱序。可以將嚴格所需看做“小於等於”,雖然實際上定義的操作可能是一個複雜的函式,但是無論如何,這個比較函式必須具備如下的基本性質:

(1)兩個關鍵字不能同時“小於等於”對方;如果k1“小於等於”k2,那麼k2決不能“小於等於”k1。

(2)如果k1“小於等於”k2,且k2“小於等於”k3,那麼k1必須“小於等於”k3.

(3)如果存在兩個關鍵字,任何一個都不“小於等於”另一個,那我們稱這兩個關鍵字是“等價的”。如果k1“等價於”k2,且k2“等價於”k3,那麼k1必須“等價於”k3。

在上面的規則中提到的“小於等於”是嚴格弱序的說法,千萬不要和我們平時使用的“<=”符號混淆了,這是一個很大的誤區,下面舉個例子。

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

class Node
{
public:
	Node(int score,int age,string name):m_score(score),m_age(age),m_name(name){}
	int getScore() { return m_score;  }
	int getScore() const { return m_score; }
	friend ostream &operator<<(ostream &out, const Node &node);
private:
	int m_score;
	int m_age;
	string m_name;
};
ostream &operator<<(ostream &out, const Node &node)
{
	out << "score:" << node.m_score << "	age:" << node.m_age << "		" 
		<< node.m_name;
	return out;
}
bool compare(const Node &node1, const Node &node2)
{
	return node1.getScore() < node2.getScore();
}

int main()
{
	Node node1 = { 32,30,"liu" };
	Node node2 = { 90,23,"hu" };
	Node node3 = { 67,32,"ki" };
	Node node4 = { 78,45,"arr" };
	vector<Node> vec;
	vec.push_back(node1);
	vec.push_back(node2);
	vec.push_back(node3);
	vec.push_back(node4);
	sort(vec.begin(), vec.end(), compare);
	for (auto itr = vec.begin(); itr != vec.end(); itr++)
	{
		cout << *itr << endl;
	}
	return 0;
}
在上面的程式示例中,我們定義了一個Node類,這個類中有三個資料,顯然這在預設的sort函式中,是沒辦法進行排序的,這個時候我們就需要自己進行比較函式的重寫。此示例中我們以分數m_score進行排序。這裡需要注意的是compare函式中,我們使用的是“<”,這裡面我們來看一下,這個小於號是不是符合我們要求。

檢驗如下:

(1)不存在數k1,k2,滿足k1<k2,k2<k1,這兩個式子中只有一個可以成立,滿足條件1

(2)對於數k1,k2,k3而言,當k1<k2,k2<k3時,則必有k1<k3,滿足條件2

(3)對於條件3,顯而易見任何對於數k1,k2而言,當k1和k2相等的時候,k1<k2和k2<k1都是不成立,k1和k2是等價的,滿足條件3。

如果我們將compare函式中的“<”符號改成“<=”號的時候,如下所示:

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

class Node
{
public:
	Node(int score,int age,string name):m_score(score),m_age(age),m_name(name){}
	int getScore() { return m_score;  }
	int getScore() const { return m_score; }
	friend ostream &operator<<(ostream &out, const Node &node);
private:
	int m_score;
	int m_age;
	string m_name;
};
ostream &operator<<(ostream &out, const Node &node)
{
	out << "score:" << node.m_score << "	age:" << node.m_age << "		" 
		<< node.m_name;
	return out;
}
bool compare(const Node &node1, const Node &node2)
{
	return node1.getScore() <= node2.getScore();
}

int main()
{
	Node node1 = { 32,30,"liu" };
	Node node2 = { 90,23,"hu" };
	Node node3 = { 67,32,"ki" };
	Node node4 = { 78,45,"arr" };
	vector<Node> vec;
	vec.push_back(node1);
	vec.push_back(node2);
	vec.push_back(node3);
	vec.push_back(node4);

	sort(vec.begin(), vec.end(), compare);
	for (auto itr = vec.begin(); itr != vec.end(); itr++)
	{
		cout << *itr << endl;
	}
	return 0;
}
這個時候如何你執行的話,會發現執行的結果和第一個程式的結果一樣啊。這時候是不是就會感覺我是在忽悠人了,如果你是這麼認為的,那我就真的好冤啊。我們仔細看一下,這個“<=”到底滿不滿足我們的要求。

檢驗如下:

(1)當k1<=k2的時候,k2<=k1是成立的,顯然不符合我們的要求1。

(2)對於數k1,k2,k3而言,當k1<=k2,k2<=k3時,則必有k1<=k3,滿足條件2

(3)對於條件3,顯而易見任何對於數k1,k2而言,k1<=k2和k2<=k1都是成立的,顯然條件3也不符合。

從上面的檢驗可以發現什麼問題?是不是有種恍然大悟的感覺,一切的焦點都落在了“=”上,這也就可以解釋為什麼我們上面的程式對於“<=”同樣沒有出錯,這不是說用“<=”沒有錯誤,而是我們的測試資料沒能檢驗出這個錯誤,這是因為在我們的資料中沒有分數相等的資料,那就永遠不會觸發這個錯誤,現在讓我們一起見證這個問題,很簡單,我們插入一個分數相同的資料,程式碼如下:

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

class Node
{
public:
	Node(int score,int age,string name):m_score(score),m_age(age),m_name(name){}
	int getScore() { return m_score;  }
	int getScore() const { return m_score; }
	friend ostream &operator<<(ostream &out, const Node &node);
private:
	int m_score;
	int m_age;
	string m_name;
};
ostream &operator<<(ostream &out, const Node &node)
{
	out << "score:" << node.m_score << "	age:" << node.m_age << "		" 
		<< node.m_name;
	return out;
}
bool compare(const Node &node1, const Node &node2)
{
	return node1.getScore() <= node2.getScore();
}

int main()
{
	Node node1 = { 32,30,"liu" };
	Node node2 = { 90,23,"hu" };
	Node node3 = { 67,32,"ki" };
	Node node4 = { 78,45,"arr" };
	Node node5 = { 78,45,"dkk" };
	vector<Node> vec;
	vec.push_back(node1);
	vec.push_back(node2);
	vec.push_back(node3);
	vec.push_back(node4);

	sort(vec.begin(), vec.end(), compare);
	for (auto itr = vec.begin(); itr != vec.end(); itr++)
	{
		cout << *itr << endl;
	}
	return 0;
}
當我們再次執行這個程式的時候,會出現什麼呢?下面請看:

                                   

開個玩笑啊,寫了這麼多文字,給大家養養眼。

        下面是真正的結果:

這裡面提示的是invalid comparator,意思是不合法的比較符,說明我們的“<=”是不合法的,這也正是我們平時程式設計時比較容易忽略和犯錯的地方,特此和大家分享一下。