c++中的std::set,是基於紅黑樹的平衡二叉樹的數據結構實現的一種容器,因爲其中所包含的元素的值是唯一的,因此主要用於去重和排序。這篇文章的目的在於探討和分享如何正確使用std::set實現去重和排序功能。
方法一:使用std::set內置的less比較函數(直接定義內置類型的set對象)
這種方法適用於:1)比較int、char等內置類型。2)只能針對某一個內置類型去重和排序:如果想通過id(int)去重,並通過hot(int)排序,該種方法就束手無策了。代碼如下:
#include <iostream>
#include <set>
using namespace std;
void main()
{
std::set<int> mySet; // 直接定義內置類型set集合
mySet.insert(10); // 默認比較函數爲less
mySet.insert(20); // 從小到大排序
for(auto it:mySet)
{
std::cout<<it<<std::endl;
}
std::cout<<"end"<<std::endl;
}
結果如下:
output:
10
20
end
方法二:自定義類(結構體)比較函數
前文提到:直接定義內置類型的set對象,即使用std::set內置的默認的less比較函數,可能不能滿足我們的實際需求。例如:現在有一批結構體對象,需要將其插入set集合,並按照id去重,按照熱度hot進行排序。這個時候,就需要重新自定義比較函數了。有兩種方法可以自定義比較函數:
重載<操作符
爲甚麼要重載<運算符呢?能不能重載”<=”或者”>=”運算符?答案是不可以。幾乎所有的方法或容器都需要排序來滿足數學意義上的標準嚴格弱序化,否則這些方法或容器的行爲將不可預知。假設f(x,y)是一個比較函數。 如果該函數滿足如下條件則它是嚴格弱序化的。
1. f(x,x) = false;
2. if f(x,y) then !f(y,x)
3. if f(x,y) and f(y,z) then f(x,z)
4. if !f(x,y)&&!f(y,x) then x==y; if x==y and y==z then x==z;
看上去有點暈乎,不過不用擔心,只要你的比較方法能夠滿足對相等元素永遠返回false(記住一個準則:永遠讓比較函數對相同元素返回false),那你的方法就滿足要求了。
其實,set容器在判定已有元素a和新插入元素b是否相等時,是這麼做的:
1)將a作爲左操作數,b作爲有操作數,調用比較函數,並返回比較值
2)將b作爲左操作數,a作爲有操作數,再調用一次比較函數,並返回比較值。
如果1、2兩步的返回值都是false,則認爲a、b是相等的,則b不會被插入set容器中
如果1、2兩步的返回值都是true,則可能發生未知行爲
因此,記住一個準則:永遠讓比較函數對相同元素返回false。
#include <iostream>
#include <set>
using namespace std;
struct song
{
int m_id;
int m_hot;
song(int id,int hot)
{
this->m_id = id;
this->m_hot = hot;
}
bool operator<(const struct song & right)const //重載<運算符
{
if(this->m_id == right.m_id) //根據id去重
return false;
else
{
if(this->m_hot != right.m_hot)
{
return this->m_hot > right.m_hot; //降序
}
else
{
return this->m_id > right.m_id;
}
}
}
};
void main()
{
std::set<song> mySet;
song s1(10,100);
song s2(20,200);
song s3(20,300);
song s4(30,200);
mySet.insert(s1); //插入s1
mySet.insert(s2); //插入s2
mySet.insert(s3); //s3和s2的id相同,不插入
mySet.insert(s4); //插入s4
for(auto it:mySet)
{
std::cout<<"id:"<<it.m_id<<",hot:"<<it.m_hot<<std::endl;
}
std::cout<<"end"<<std::endl;
};
結果如下:
id:30,hot : 200
id:20,hot : 200
id:10,hot : 100
end
重載()運算符
具體代碼如下:
#include <iostream>
#include <set>
using namespace std;
struct song
{
int m_id;
int m_hot;
song(int id,int hot)
{
this->m_id = id;
this->m_hot = hot;
}
/*
bool operator<(const struct song & right)const //重載<運算符
{
if(this->m_id == right.m_id) //根據id去重
return false;
else
{
if(this->m_hot != right.m_hot)
{
return this->m_hot > right.m_hot; //降序
}
else
{
return this->m_id > right.m_id;
}
}
}
*/
};
struct comp
{
bool operator()(struct song left,struct song right) //重載()運算符
{
if(left.m_id == right.m_id) //根據id去重
return false;
else
{
if(left.m_hot != right.m_hot)
{
return left.m_hot > right.m_hot; //降序
}
else
{
return left.m_id > right.m_id;
}
}
}
};
void main()
{
std::set<song,comp> mySet; //寫法和2.1中的的區別
song s1(10,100);
song s2(20,200);
song s3(20,300);
song s4(30,200);
mySet.insert(s1); //插入s1
mySet.insert(s2); //插入s2
mySet.insert(s3); //s3和s2的id相同,不插入
mySet.insert(s4); //插入s4
for(auto it:mySet)
{
std::cout<<"id:"<<it.m_id<<",hot:"<<it.m_hot<<std::endl;
}
std::cout<<"end"<<std::endl;
};
複製代碼
結果如下:
id:30,hot : 200
id:20,hot : 200
id:10,hot : 100
end