STL中集合的并、交、差 运算
声明
- 今天一直在学习STL, 学习目录参考的是C语言编程网, 内容主要参考的是这个网站。 我学习的流程是按照C语言编程网的目录结构, 学到哪一个知识点就去www.cplusplus.com这个网站搜, 他网站自带的搜索不太好用, 我有时候会搜不到内容, 可以在必应搜索中指定网站搜索, 比如搜索set就按照set site:www.cplusplus.com这种关键词来搜索。
- 主要是记录下来供自己日后参考, 因为一段时间不用就忘了, 所以是按照自己容易理解的方式写的, 不过任然希望各位可以指出错误和欠缺的地方, 共同进步。
集合的介绍
模板定义
- 模板(文档连接)
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, 不考虑multiset), 联想高中学习的集合的3个性质, 无序性、确定性和互异性。在c++的集合中, 可以保证确定性和互异性, 但是没有无序性, 在set中, 元素的存储是有序的, 使用的比较方法就是上面模板中的less<T>。
集合的其他使用方法不做介绍了, 这里仅介绍集合的3个运算方法, 并、交、差。在C++中分别是set_union(), set_intersection(), set_difference().下面重点介绍并集, 其它两个基本一样, 就换了个名字。
并集运算
模板定义
- 并集有两种使用方法,分别是(文档连接):
// 1
template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator set_union (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result);
// 2
template <class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
OutputIterator set_union (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp);
- 比较函数 comp 可以不指定, 一般情况下使用第一种即可。但是当使用自定义类型是就需要进行制定了,这个下面再说。
使用举例
int类型set使用举例
- 由上面的模板定义可以看出, set_union()接受5个或6个参数, 前两个参数指出一个容器的开始和结束的位置, 接下来的两个参数指出另一容器的开始和结束的位置, 第五个参数指出需要插入容器的迭代器, 第六个参数是一个比较函数, 在使用已经有比较函数的类型的时候, 可以没有。
- 第五个参数可以通过insert_iterator获取, 参考它的文档,观察其构造函数:
explicit insert_iterator (Container& x, typename Container::iterator i) : container(&x), iter(i) {}
知道该函数接受两个参数, 第一个参数是一个容器, 第二个参数是一个迭代器。当然也可以使用下面这种方式获取迭代器:
auto iter = std::insert(result, std::begin(result));
这个也是参考网站上示例的方式。
- 根据上面的分析, 可以写出下面的代码
#include <iostream>
#include <string>
#include <utility> // for pair<> & make_pair<>()
#include <set>
#include <algorithm>
#include <iterator>
using std::cout;
using std::endl;
using std::string;
using intSet = std::set<int>;
void setUnion()
{
intSet first = { 5, 10, 15, 20, 25 };
intSet second = { 6 ,11, 16, 21, 26, 5, 15 ,20 };
cout << "第一个集合中的元素时:\n";
for (auto it : first) { cout << it << " "; }cout << endl;
cout << "第二个集合中的元素时:\n";
for (auto it : second) { cout << it << " "; }cout << endl;
intSet result;
std::insert_iterator<intSet> it(result, result.begin());
std::set_union(first.begin(), first.end(), second.begin(), second.end(), it);
cout << "集合的并集是:" << endl;
for (auto it : result) { cout << it << " "; }cout << endl;
}
自定义类型set使用举例
- 我们先自定义一个简单的类型
class People
{
public:
People(const string& fname, const string& sname):m_firstname(fname), m_secondname(sname) {}
People() = default;
friend std::ostream& operator<<(std::ostream& out, const People& people);
friend std::istream& operator>>(std::istream& in, People& people); // 方便输出
// 将名字打包返回, 方便下一个进行名字的比较函数, 那个函数没有声明为友元函数, 所以无法直接访问该类的私有变量
// 此处切不可返回引用, 只能使用值返回的方式, 否则会报错
std::pair<string, string> getName() const
{
pair<string, string> ret;
ret = std::make_pair(m_firstname, m_secondname);
return ret;
}
private:
string m_firstname;
string m_secondname;
};
std::ostream& operator<<(std::ostream& out, const People& people)
{
out << people.m_firstname << " " << people.m_secondname;
return out;
}
std::istream& operator>>(std::istream& in, People& people)
{
in >> people.m_firstname >> people.m_secondname;
return in;
}
- 这个类有两个私有变量, m_firstname, 和 m_secondname。可以在类中直接重载<运算符作为比较函数, 像下面的代码所示, 如果在类内重载<运算符的话, 就不需要另外指定比较函数了。
bool operator<(const People& otherPeople) const
{
return m_secondname < otherPeople.m_secondname || m_secondname == otherPeople.m_secondname && m_firstname < otherPeople.m_firstname;
}
- 但是为了理解set_union()第六个参数的意义, 我采用在外部自定义一个比较函数的方式。如下所示:
struct peopleComp
{
bool operator()(const People& people1, const People& people2)
{
std::pair<string, string> lname;
std::pair<string, string> rname;
lname = people1.getName();
rname = people2.getName();
return lname.second < rname.second || lname.second == rname.second && lname.first < rname.first;
}
}pComp;
- 然后就可以使用下面的函数来进行集合的并集运算了
void setTest()
{
using peopleSet = std::set<People, peopleComp>;
peopleSet first = { {"liu", "bei"}, {"guan", "yu"}, {"zhang", "fei"} };
peopleSet second = { {"liu", "bei"}, {"cao", "cao"}, {"sun", "quan"} };
peopleSet result;
cout << "第一个集合中的元素是:\n";
for (auto it : first) { cout << it << " "; }cout << endl;
cout << "第二个集合中的元素是:\n";
for (auto it : second) { cout << it << " "; }cout << endl;
std::insert_iterator<peopleSet> it(result, result.begin());
set_union(first.begin(), first.end(), second.begin(), second.end(), it, pComp);
cout << "二者的并集是:\n";
for (auto it : result) { cout << it << " "; }cout << endl;
}
- 结果大概如下图所示
总结
- 交集和差集的使用类似, 只是换了个名字。上面就是我对如何使用并、交、差运算方法的总结。当然于我而言最重要的并不是会使用这三种运算的基本语法, 而是找到了一种学习STL基础知识的方式。 于我而言, 最重要的两个资源就是C语言中文网和www.cplusplus.com这两个网站。 对于没有太长时间嚼书, 想要在短时间内掌握STL基础知识来说还是不错的, C语言中文网提供学习框架,www.cplusplus.com提供学习内容和示例。当然当有了时间之后还是希望自己可以比较系统完全的学习一下STL的,不过更多的还是要在实践中学习。