c++中的std::set,是基於紅黑樹的平衡二叉樹的數據結構實現的一種容器,由於其中所包含的元素的值是惟一的,所以主要用於去重和排序。這篇文章的目的在於探討和分享如何正確使用std::set實現去重和排序功能。ios
1.方法一:使用std::set內置的less比較函數(直接定義內置類型的set對象)c++
這種方法適用於:1)比較int、char等內置類型。2)只能針對某一個內置類型去重和排序:若是想經過id(int)去重,並經過hot(int)排序,該種方法就一籌莫展了。代碼以下:數據結構
1 #include <iostream> 2 #include <set> 3 using namespace std; 4 void main() 5 { 6 std::set<int> mySet; // 直接定義內置類型set集合 7 mySet.insert(10); // 默認比較函數爲less 8 mySet.insert(20); // 從小到大排序 9 for(auto it:mySet) 10 { 11 std::cout<<it<<std::endl; 12 } 13 std::cout<<"end"<<std::endl; 14 15 }
結果以下:
output: 10 20 end
2.方法二:自定義類(結構體)比較函數less
前文提到:直接定義內置類型的set對象,即便用std::set內置的默認的less比較函數,可能不能知足咱們的實際需求。例如:如今有一批結構體對象,須要將其插入set集合,並按照id去重,按照熱度hot進行排序。這個時候,就須要從新自定義比較函數了。有兩種方法能夠自定義比較函數:函數
2.1 重載<操做符this
爲甚麼要重載<運算符呢?能不能重載"<="或者">="運算符?答案是不能夠。幾乎全部的方法或容器都須要排序來知足數學意義上的標準嚴格弱序化,不然這些方法或容器的行爲將不可預知。假設f(x,y)是一個比較函數。 若是該函數知足以下條件則它是嚴格弱序化的。spa
1.f(x,x) = false; code
2. if f(x,y) then !f(y,x)對象
3.if f(x,y) and f(y,z) then f(x,z)blog
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做爲有操做數,再調用一次比較函數,並返回比較值。若是一、2兩步的返回值都是false,則認爲a、b是相等的,則b不會被插入set容器中;若是一、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
2.2 重載()運算符
具體代碼以下:
1 #include <iostream> 2 #include <set> 3 using namespace std; 4 struct song 5 { 6 int m_id; 7 int m_hot; 8 song(int id,int hot) 9 { 10 11 this->m_id = id; 12 this->m_hot = hot; 13 } 14 /* 15 bool operator<(const struct song & right)const //重載<運算符 16 { 17 if(this->m_id == right.m_id) //根據id去重 18 return false; 19 else 20 { 21 if(this->m_hot != right.m_hot) 22 { 23 return this->m_hot > right.m_hot; //降序 24 } 25 else 26 { 27 return this->m_id > right.m_id; 28 } 29 } 30 } 31 */ 32 }; 33 struct comp 34 { 35 bool operator()(struct song left,struct song right) //重載()運算符 36 { 37 38 if(left.m_id == right.m_id) //根據id去重 39 return false; 40 else 41 { 42 if(left.m_hot != right.m_hot) 43 { 44 return left.m_hot > right.m_hot; //降序 45 } 46 else 47 { 48 return left.m_id > right.m_id; 49 } 50 51 } 52 } 53 54 }; 55 void main() 56 { 57 std::set<song,comp> mySet; //寫法和2.1中的的區別 58 song s1(10,100); 59 song s2(20,200); 60 song s3(20,300); 61 song s4(30,200); 62 mySet.insert(s1); //插入s1 63 mySet.insert(s2); //插入s2 64 mySet.insert(s3); //s3和s2的id相同,不插入 65 mySet.insert(s4); //插入s4 66 for(auto it:mySet) 67 { 68 std::cout<<"id:"<<it.m_id<<",hot:"<<it.m_hot<<std::endl; 69 } 70 std::cout<<"end"<<std::endl; 71 };
結果以下:
id:30,hot : 200 id:20,hot : 200 id:10,hot : 100end