目录
5.6.4 set_symmetric_difference() 对称差集算法
5.1 set容器
定义set容器的模板如下:
set<T>,保存对象唯一,有序
multiset<T>,可以保存重复对象。
unordered_set<T>,对象唯一,位置由哈希值决定。
unordered_multiset<T>,可以保存重复对象。
5.2 使用set<T>容器
set容器内部元素的组织方式和map<T>相同。都是平衡二叉树。
1.set没有at() ioerator[]()
2.insert() emplace() emplace_hint()
3.clear() erase()
4.find() equal_range() lower_bound() upper_bound()
5.2.4 set 迭代器
5.所有set<T>成员函数返回的迭代器都是const,如果想要修改set容器中的元素,必须先删除它,再插入新的版本。
当必须修改元素且需要将他们组合到一个或多个set容器中时,可以使用指针。通常使用shared_ptr或weak_ptr。
5.2.5 在set中保存指针
大多数时候我们不在意元素在set中的顺序,只在意在不在。
推荐使用memory头文件中的owner_less<T>函数对象类型的实例来比较容器中的智能指针。
比较基于两个智能指针所拥有对象的地址。
shared_ptr<T>类模板中owner_before<T>实例的函数模板原型:
• template<typename X> bool owner_before(const std::shared_ptr<X>& other) const;
• template<typename X> bool owner_before(const std::weak_ptr<X>& other) const;
weak_ptr类似。注意函数模板类型参数和类模板类型参数不同,所以可以比较指向不同类型对象的指针。
1.在容器中保存weak_ptr对象时,在使用它时需要确保它以来的shared_ptr仍然存在。可以通过expired()来检查和他关联的shared_ptr是否存在。
5.3 使用multimap
默认用less<T>来比较元素,也可以使用不同的比较函数,但是在元素等价,必须返回false:
std::multiset<string, std::greater<string>> words{ {"dog", "cat", "mouse"},
std::greater<string>()};
在一个有比较运算符comp的multimap容器中,如果表达式!(a comp b)&& !(b comp a) 为true,那么a和b相等。
1.insert总是可以成功。
2.emplace() emplace_hint()
3.find()返回与参数匹配的第一个元素的迭代器,如果都不匹配,返回结束迭代器。
4.equal_range()
5.lower_bound() upper_bound()
6.count()
5.3.1 保存派生类指针
如果不在乎元素的排序顺序,可以使用owner<T>实例。
5.4 unordered_set
和unordered_map<T>容器。
可以将格子的索引传入begin() end()或 cbegin() cend(),返回格子所包含元素段的开始和结束迭代器。
成员函数bucket_count()返回格子的个数。
以下代码展示如何列出容器中每个格子的元素。
for(size_t bucket_index {}; bucket_index < names.bucket_count(); ++bucket_index)
{
std::cout << "Bucket " << bucket_index << ":\n";
for(auto iter = names.begin(bucket_index); iter != names.end(bucket_index); ++iter)
{
std::cout << " " << iter->first << " " << iter->second;
}
std::cout << std::endl;
}
5.6 集合运算
5.6.1 set_union() 算法
第一个版本5个参数:前两个参数指出左操作数的集合范围,三四参数指出右操作数的集合范围。最后一个指向结果集合的存放位置。
要求使用set_union 之前 set1set2需要升序。
std::vector<int> set1 {1, 2, 3, 4, 5, 6};
std::vector<int> set2 {4, 5, 6, 7, 8, 9};
std::vector<int> result;
std::set_union(std::begin(set1), std::end(set1), // Range for set that is left operand
std::begin(set2), std::end(set2), // Range for set that is right operand
std::back_inserter(result)); // Destination for the result: 1 2 3 4 5 6 7 8 9
第二个版本6个参数:最后一个参数是用来比较集合元素的函数对象。
std::set<int, std::greater<int>> set1 {1, 2, 3, 4, 5, 6}; // Contains 6 5 4 3 2 1
std::set<int, std::greater<int>> set2 {4, 5, 6, 7, 8, 9}; // Contains 9 8 7 6 5 4
std::set<int, std::greater<int>> result; // Elements in descending sequence
std::set_union(std::begin(set1), std::end(set1),std::begin(set2), std::end(set2),
std::inserter(result, std::begin(result)), // Result destination: 9 8 7 6 5 4 3 2 1
std::greater<int>()); // Function object for comparing elements
set_union()返回一个指向被复制元素段末尾的后一个位置的迭代器。
5.6.2 set_intersection() 算法
两个版本的set_intersection和set_union参数相同。
结果容器默认用less<T>对元素进行排序。
set_intersection返回一个指向目的容器中插入的最后一个元素的下一个位置。
std::set<string> words1 {"one", "two", "three", "four", "five", "six"};
std::set<string> words2 {"four", "five", "six", "seven", "eight", "nine"};
std::set<string> result;
std::set_intersection(std::begin(words1), std::end(words1),
std::begin(words2), std::end(words2),
std::inserter(result, std::begin(result)));
// Result: "five" "four" "six"
5.6.3 set_difference() 算法
返回一个指向目的容器中插入的最后一个元素的下一个位置。
std::set<string, std::greater<string>> words1 {"one", "two", "three", "four", "five", "six"};
std::set<string, std::greater<string>> words2 {"four", "five", "six", "seven", "eight", "nine"};
std::set<string, std::greater<string>> result;
std::set_difference(std::begin(words1), std::end(words1),
std::begin(words2), std::end(words2),
std::inserter(result, std::begin(result)), // Result: "two" "three" "one"
std::greater<string>()); // Function object to compare element
5.6.4 set_symmetric_difference() 对称差集算法
std::set<string> words1 {"one", "two", "three", "four", "five", "six"};
std::set<string> words2 {"four", "five", "six", "seven", "eight", "nine"};
std::set_symmetric_difference(std::begin(words1), std::end(words1),
std::begin(words2), std::end(words2),
std::ostream_iterator<string> {std::cout, " "});
5.6.5 includes() 算法
比较两个元素的集合,如果第二个集合中全部元素来自于第一个集合,返回true。如果第二个集合为空,也返回true。
std::set<string> words1{ "one", "two", "three", "four", "five", "six" };
std::set<string> words2{ "four", "two", "seven" };
std::multiset<string> words3;
std::cout << std::boolalpha
<< std::includes(std::begin(words1), std::end(words1),
std::begin(words2), std::end(words2))
<< std::endl; // Output: false
std::cout << std::boolalpha
<< std::includes(std::begin(words1), std::end(words1),
std::begin(words2), std::begin(words2))
<< std::endl; // Output: true
std::set_union(std::begin(words1), std::end(words1), std::begin(words2), std::end(words2),
std::inserter(words3, std::begin(words3)));
std::cout << std::boolalpha
<< std::includes(std::begin(words3), std::end(words3),
std::begin(words2), std::end(words2))
<< std::endl; // Output: true
copy(begin(words3), end(words3), ostream_iterator<string>(cout, " "));
并集操作只包含每个重复元素的一个副本。如果words1 words2是multiset容器,那结果肯会包含一些重复元素。
std::multiset<string> words1{ "one", "two","nine","nine","one", "three", "four", "five", "six" };
std::multiset<string> words2{ "four", "two", "seven","seven","nine","nine"};
std::multiset<string> words3;
std::cout << std::boolalpha
<< std::includes(std::begin(words1), std::end(words1),
std::begin(words2), std::end(words2))
<< std::endl; // Output: false
std::cout << std::boolalpha
<< std::includes(std::begin(words1), std::end(words1),
std::begin(words2), std::begin(words2))
<< std::endl; // Output: true
std::set_union(std::begin(words1), std::end(words1), std::begin(words2), std::end(words2),
std::inserter(words3, std::begin(words3)));
std::cout << std::boolalpha
<< std::includes(std::begin(words3), std::end(words3),
std::begin(words2), std::end(words2))
<< std::endl; // Output: true
copy(begin(words3), end(words3), ostream_iterator<string>(cout, " "));
如果在两个集合中都重复,结果也会重复。
课后练习
pe5_1:
#include<iostream>
#include<vector>
#include<set>
#include<random>
#include<string>
enum class Color { red1, red2, black1, black2 };
static const std::string scolor[4]={ "red1", "red2", "black1", "black2" };
//const string scolor[4] = { "red1", "red2", "black1", "black2" };
using namespace std;
template<typename T>
T generate_rand(const T& min,const T& max);
class Card {
private:
Color color;
int number;
public:
//Card() :color(Color::red1), number(0) {};
Card(Color cc, int nn) :color(cc), number(nn) {};
Color get_color() { return color; };
int get_number() { return number; };
bool operator<(const Card& cc) const {
return color < cc.color || (color == cc.color && number < cc.number);
}
friend std::ostream& operator<<(ostream &os, const Card& cc) {
//static const string scolor[4] = { "red1", "red2", "black1", "black2" };
os << scolor[int(cc.color)] <<" "<<cc.number << endl;
return os;
}
};
int main() {
vector<Card> cc;
set<Card> AA[4];
/*set<Card> north;
set<Card> south;
set<Card> east;
set<Card> west;
AA[0] = north;
AA[1] = south;
AA[2] = east;
AA[3] = west;*/
/*for (int i = 0; i < 52; i++) {
cc.push_back(Card{ generate_rand(Color::red1,Color::black2), generate_rand(1,13) });
}*/
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 13; j++) {
cc.emplace_back(static_cast<Color>(i), j);
}
}
random_shuffle(begin(cc), end(cc));
auto iter = begin(cc);
for (int i = 0; i < 4; i++) {
copy(iter, iter + 13, inserter( AA[i],begin( AA[i]) ) );
advance(iter, 13);
}
for (int i = 0; i < 4; i++) {
cout << "玩家" << i << " : " << endl;
for (auto value : AA[i]) {
std::cout << value << endl;
}
}
return 0;
}
template<typename T>
T generate_rand(const T& min, const T& max) {
// 创建一个随机数引擎
std::random_device rd;
std::mt19937 gen(rd());
// 定义随机数的范围
/*int min = 1;
int max = 52;*/
// 创建一个分布对象,指定随机数的范围
std::uniform_int_distribution<> dis(min, max);
// 生成随机数
/*int random_num = dis(gen);*/
return dis(gen);
}
pe5_2:
#include<iostream>
#include<set>
#include<unordered_set>
using namespace std;
int main() {
unordered_multiset<string> words;
string word;
while (true)
{
if ((cin >> word).eof()) {
cin.clear();
break;
}
words.emplace(word);
}
int ccount = 0;
for (auto value : words) {
cout << value << " count: " << words.count(value)<<" ";
if ((ccount+1) % 6 == 0) {
cout << endl;
ccount == 0;
}
ccount++;
}
return 0;
}
pe5_3:
#include<iostream>
#include<set>
#include<unordered_set>
#include<random>
using namespace std;
template<typename T>
T generate_rand(const T& min, const T& max);
template<typename T>
T sum(const T& min, const T& max);
int main() {
multiset<short> words;
short number1 = 1, number2 =6;
for (int i = 0; i < 1000; i++) {
words.emplace(sum(generate_rand(number1, number2), generate_rand(number1, number2)));
}
for (int i = 2; i < 13;i++) {
cout << i << " count: " << words.count(i) << endl;
}
return 0;
}
template<typename T>
T generate_rand(const T& min, const T& max) {
// 创建一个随机数引擎
std::random_device rd;
std::mt19937 gen(rd());
// 定义随机数的范围
/*int min = 1;
int max = 52;*/
// 创建一个分布对象,指定随机数的范围
std::uniform_int_distribution<> dis(min, max);
// 生成随机数
/*int random_num = dis(gen);*/
return dis(gen);
}
template<typename T>
T sum(const T& min, const T& max) {
return min + max;
}
pe5_4:
#include<iostream>
#include<set>
#include<unordered_set>
#include<random>
#include<vector>
#include<map>
#include<tuple>
using namespace std;
const vector<string> books{ "C++","C","Java","Python","PHP","GOLONG","R","shell","C#","Ruby" };
template<typename T>
T generate_rand(const T& min, const T& max);
set<string> generate_set(const int& min, const int& max);
int main() {
multimap<string, set<string>> people_books;
cout << "Enter people's name: " << endl;
string name;
while (true) {
if ((cin >> name).eof()) {
cin.clear();
break;
}
people_books.emplace(name, generate_set(0, 9));
}
cout << "Show All;" << endl;
for (auto value : people_books) {
cout << value.first << " has " << "books: ";
for (auto vvv : value.second) {
cout << vvv << " ";
}
cout << endl;
}
multimap<int, tuple<string,string,set<string>>> number_books;
set<string> same_books;
for (auto iter = begin(people_books); iter != end(people_books); iter++) {
for (auto iter2 = next(iter); iter2 != end(people_books); iter2++) {
set_intersection(begin(iter->second), end(iter->second),
begin(iter2->second), end(iter2->second),
inserter(same_books, begin(same_books)));
number_books.emplace(size(same_books), tuple<string, string, set<string>>(iter->first, iter2->first, same_books));
}
}
/*for (auto value : people_books) {
for (auto value2 : people_books) {
set_intersection(begin(value.second), end(value.second),
begin(value2.second), end(value2.second),
inserter(same_books,begin(same_books)));
number_books.emplace(size(same_books), tuple<string, string, set<string>>(value.first, value2.first, same_books));
}
}*/
cout << "Show Compare: " << endl;
for (auto value : number_books) {
if (value.first >= 2) {
cout << get<0>(value.second) << " and " << get<1>(value.second) << " has " << value.first
<< " they are: ";
for (auto vvv : get<2>(value.second)) {
cout << vvv << " ";
}
cout << endl;
}
else continue;
}
return 0;
}
template<typename T>
T generate_rand(const T& min, const T& max) {
// 创建一个随机数引擎
std::random_device rd;
std::mt19937 gen(rd());
// 定义随机数的范围
/*int min = 1;
int max = 52;*/
// 创建一个分布对象,指定随机数的范围
std::uniform_int_distribution<> dis(min, max);
// 生成随机数
/*int random_num = dis(gen);*/
return dis(gen);
}
set<string> generate_set(const int& min, const int& max) {
set<string> pbset;
string name;
int rand_times = generate_rand(4, 6);
for (int i = 0; i < rand_times; i++) {
pbset.emplace(books[generate_rand(0, 9)]);
}
return pbset;
}