一、set定义

c++官方对set的定义如下:

Set:Sets are containers that store unique elements following a specific order.
Set:set是一类用来存储满足一定排序的独一元素的容器。

类模板如下:

template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare
           class Alloc = allocator<T>      // set::allocator_type
           > class set;

可以看到set模板类有三个参数:

  1. T:set容器中的元素类型。
  2. Compare:比较类,定义容器中元素排序,缺省为less< T >。
  3. Alloc:内存分配器。一般使用缺省值。

二、set< int >实例

	set<int, less<int> > set_less;
	set<int, greater<int> > set_greater;
	for(int i=0; i<10; i++){
		set_less.insert(i%3);
		set_greater.insert(i%3);
	}
	// 遍历
	for(set<int, less<int> >::iterator iter=set_less.begin(); iter!=set_less.end(); iter++){
		cout<<*iter<<" ";
	}
	cout<<endl;
	for(set<int, greater<int> >::iterator iter=set_greater.begin(); iter!=set_greater.end(); iter++){
		cout<<*iter<<" ";
	}

输出为:

0 1 2 
2 1 0 

三、set< pair< int , int>>实例

1.默认pair<int, int>排序
set<pair<int, int> > set_pair;
for(int i=0; i<10; i++){
	set_pair.insert(pair<int, int>(i, i%3));
}
for(set<pair<int, int> >::iterator iter=set_pair.begin(); iter!=set_pair.end(); iter++){
	cout<<"("<<iter->first<<","<<iter->second<<") ";
}
cout<<endl;

输出为:

(0,0) (1,1) (2,2) (3,0) (4,1) (5,2) (6,0) (7,1) (8,2) (9,0) 
2.使用less、greater排序

使用less:

set<pair<int, int>, less<pair<int, int>> > set_pair_less;
for(int i=0; i<10; i++){
	set_pair_less.insert(pair<int, int>(i, i%3));
}
for(set<pair<int, int>, less<pair<int, int>> >::iterator iter=set_pair_less.begin(); iter!=set_pair_less.end(); iter++){
	cout<<"("<<iter->first<<","<<iter->second<<") ";
}
cout<<endl;

输出为:

(0,0) (1,1) (2,2) (3,0) (4,1) (5,2) (6,0) (7,1) (8,2) (9,0) 

使用greater:

set<pair<int, int>, greater<pair<int, int>> > set_pair_greater;
for(int i=0; i<10; i++){
	set_pair_greater.insert(pair<int, int>(i, i%3));
}
for(set<pair<int, int>, greater<pair<int, int>> >::iterator iter=set_pair_greater.begin(); iter!=set_pair_greater.end(); iter++){
	cout<<"("<<iter->first<<","<<iter->second<<") ";
}
cout<<endl;

输出为:

(9,0) (8,2) (7,1) (6,0) (5,2) (4,1) (3,0) (2,2) (1,1) (0,0) 

使用less<>、greater<>模板对pair< int , int >排序时,先比较pair的第一个元素,如果第一个元素相等再比较第二个元素。

3.自定义排序

例如,我们使用pair<char, int>记录字符串中字符的顺序时(实际上使用map<char, int>容器记录会更方便),希望set中的字符按照频率升序排列(频率相同时,顺序不做要求),几种自定义set排序的代码如下:

#include <set>
#include <iostream>
#include <vector>

using namespace std;

class Cmp {
public:
    // 重载 () 操作符
    // 参数必须加上const限定符
    bool operator()(const pair<char, int>& a, const pair<char, int>& b) const {
        return a.second == b.second ? (a.first < b.first) : (a.second < b.second);
    }
};
int main() {
    string str = "icaneatglass";
    vector<int> vec(26, 0);
    for (int i = 0; i < str.length(); i++) {
        vec[str[i] - 'a'] ++;
    }
    // 使用仿函数自定义set排序
    set<pair<char, int>, Cmp> set_pair_functor;
    // 使用lambda表达式自定义排序
    auto cmp = [](const pair<char, int>& a, const pair<char, int>& b) {
        return a.second == b.second ? (a.first < b.first) : (a.second < b.second);
    };

    set<pair<char, int>, decltype(cmp)> set_pair_lambda(cmp);
    // 插入数据
    for (int i = 0; i < 26; i++) {
        if (vec[i] != 0) {
            set_pair_functor.insert(pair<char, int>((char)('a' + i), vec[i]));
            set_pair_lambda.insert(pair<char, int>((char)('a' + i), vec[i]));
        }
    }
    // 输出
    for (auto it : set_pair_functor) {
        cout << "(" << it.first << "," << it.second << ")";
    }
    cout << endl;
    for (auto it : set_pair_lambda) {
        cout << "(" << it.first << "," << it.second << ")";
    }
    cout << endl;
    return 0;
}

输出为:

(c,1)(e,1)(g,1)(i,1)(l,1)(n,1)(t,1)(s,2)(a,3)
(c,1)(e,1)(g,1)(i,1)(l,1)(n,1)(t,1)(s,2)(a,3)

需要注意的是,尽管排序时我们只需要考虑pair< char, int>中的第二个参数,频率(int);不需要考虑第一个参数,字符(char)。但是在仿函数Cmp的()运算符重载中,也必须进行字符比较。
如果写成如下(下面的代码是错误的):

class Cmp{
public:
	bool operator()(const pair<char, int> &a, const pair<char, int> &b) const{
		return a.second<b.second;
	}
};
// 其他部分代码都相同
...

最终的输出为:

(c,1) (s,2) (a,3) 

这是因为:前面已经提到set中只会 store unique elements,即对于"相等"的元素set中只存储一个。而set中用Cmp类来判断两个元素是否equal。假如两个元素a和b,Cmp(a,b)返回false,Cmp(b,a)也返回false,那么就认为a和b相等。假如a和b的second(在本例中代表字符的频率)相等,那么set会只存储最后插入的元素,这就导致最终set中只会存储second(字符的频率)不同的元素。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐