set基本概念
set也叫做集合,它的特点就是所有的元素在插入的时候会自动完成排序(默认是升序排列)。
set在物理空间上也不是连续的,所以它就不支持随机存取(利用下标), 它的迭代器也不支持指针算术运算,只能进行++和--。
set的底层的数据结构是红黑树。
可以添加删除元素,但是禁止修改元素,因为修改会导致数的结构发生变化,导致红黑树的结构被破坏,所以set的迭代器是const,set调用的函数也是常函数。
set和multiset的区别
set不允许有重复的元素
multiset允许有重复的元素
方法的使用基本相同,数据结构也相同
树结构的简介
二叉树
任何节点最多只允许有两个子节点,分别是左子节点和右子节点
二叉树的搜索
二叉树中的节点按照一定的规则进行排序,访问效率会更高一些
二叉树放置规则
任何子节点的元素值一定大于左子树中每个节点的元素值,并且小于右子树的值;
所以从根节点一直向左,无路可走时就是最小值,向右无路可走就是最大值。
平衡二叉树
当二叉树的左子树和右子树不平衡的时候,搜索的大小值的时间就会不同,所以会降低我们的搜索效率,所以需要平衡二叉树来保证树的相对平衡;
平衡二叉树左子树的高度和右子树的高度的绝对值要小于等于1。红黑树是一种特殊的平衡二叉树。因此搜索效率会更高。
红黑树
特点:
1、每个节点不是红色就是黑色
2、根节点是黑色的
3、如果一个节点是红色,它的两个子节点都是黑色
4、对于每一个节点,从该节点开始到其所有的后代节点上,均包含相同数目的黑色节点
5、每个叶子节点都是黑色的(叶子节点是空节点)
set的常用方法
特别注意:不可以修改集合元素的值
set构造和赋值
功能描述:创建set容器以及赋值
构造:
set st; //默认构造函数:
set(const set &st); //拷贝构造函数
set<T, 仿函数> st; // 仿函数来制定排序规则,默认升序
赋值:
set& operator=(const set &st); //重载赋值
#include <set>
void printSet(set<int> & s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//构造和赋值
void test01()
{
set<int> s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
printSet(s1);
//拷贝构造
set<int>s2(s1);
printSet(s2);
//赋值
set<int>s3;
s3 = s2;
printSet(s3);
}
总结:
set容器插入数据时用insert
set容器插入数据的数据会自动排序
set大小和交换
功能描述:统计set容器大小以及交换set容器
函数原型:
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
swap(st); //交换两个集合容器
//大小
void test01()
{
set<int> s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
if (s1.empty())
{
cout << "s1为空" << endl;
}
else
{
cout << "s1不为空" << endl;
cout << "s1的大小为: " << s1.size() << endl;
}
}
//交换
void test02()
{
set<int> s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
set<int> s2;
s2.insert(100);
s2.insert(300);
s2.insert(200);
s2.insert(400);
cout << "交换前" << endl;
printSet(s1);
printSet(s2);
cout << endl;
cout << "交换后" << endl;
s1.swap(s2);
printSet(s1);
printSet(s2);
}
总结:
统计大小 --- size
判断是否为空 --- empty
交换容器 --- swap
set插入和删除
功能描述:set容器进行插入数据和删除数据
函数原型:
insert(elem); //在容器中插入元素。
clear(); //清除所有元素
erase(pos) // 删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end) // 删除区间[beg,end)中的元素,返回下一个元素的迭代器
erase(elem); // 删除容器中值为elem的元素
#include <iostream>
#include <set>
using namespace std;
// 定义通用的打印set的函数模板
template<class T>
void printSet(set<T>& s)
{
for (auto ele : s)
{
cout << ele << " ";
}
cout << endl;
}
// 迭代器只有在删除时才会失效
// 插入数据的时候不会导致迭代器失效
void test02()
{
set<int> s = { 2,3,5,6 };
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
if (*it == 5)
{
//it = s.erase(it); // 删除的时候要更新迭代器
s.insert(it, 0); // 插入不会失效
}
}
printSet(s);
}
总结:
插入 --- insert
删除 --- erase
清空 --- clear
set查找和统计
功能描述:对set容器进行查找数据以及统计数据
函数原型:
find(ele); // 找到并返回迭代器的位置,找不到返回end
count(ele); // 统计ele元素出现的次数,对于set来说元素不能重复,所以结果只有1或者0
// 查找和统计
void test03()
{
set<int> s1 = { 40,20,30,10 };
set<int>::iterator pos = s1.find(30);
if (pos != s1.end())
{
cout << "找到了,结果是:" << *pos << endl;
}
else
{
cout << "没找到" << endl;
}
cout << s1.count(30) << endl;
cout << s1.count(50) << endl;
}
总结:
查找 --- find (返回的是迭代器)
统计 --- count (对于set,结果为0或者1)
set迭代器失效的问题
map、multimap、set、multiset 删除当前的迭代器的时候会失效,只要在earse的时候更新当前的iterator即可。
// 迭代器只有在删除时才会失效
// 插入数据的时候不会导致迭代器失效
void test02()
{
set<int> s = { 2,3,5,6 };
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
if (*it == 5)
{
//it = s.erase(it); // 删除的时候要更新迭代器
s.insert(it, 0); // 插入不会失效
}
}
printSet(s);
}
set和multiset区别
set不可以插入重复数据,而multiset可以
set插入数据的同时会返回插入结果,表示插入是否成功
multiset不会检测数据,因此可以插入重复数据
// multiset成员函数和set一样,不同点在于multiset允许重复元素存在
void test04()
{
multiset<int> ms = { 20,10,30,20 };
for (auto ele : ms)
{
cout << ele << " ";
}
cout << endl;
cout << "20数字出现的次数:" << ms.count(20) << endl;
}
总结:
如果不允许插入重复数据可以利用set
如果需要插入重复数据利用multiset
set的练习
// 手写一个函数对象
template<class T>
class MyCompare
{
public:
// 实现()运算符的重载
bool operator()(T v1, T v2)const // 要成名为常成员函数,因为set禁止修改元素的,属于常对象
{
return v1 > v2;
}
};
// 利用函数模板来遍历用仿函数排序后的set容器
template<class T>
void printSet(set<T, MyCompare<T>>& s)
{
for (auto ele : s)
{
cout << ele << " ";
}
cout << endl;
}
void test05()
{
set<int> s1 = { 40,10,20,30 };
printSet(s1);
set<int, MyCompare<int>> s2 = { 40,10,20,30 };
printSet(s2);
set<int, greater<int>> s3 = { 40,10,20,30 };
for (auto ele : s3)
{
cout << ele << " ";
}
cout << endl;
}
// 对于自定义的数据类型,必须指定排序规则才能插入数据,使用仿函数来进行确定
class Person
{
public:
string m_Name;
int m_Age;
Person(string name, int age) :m_Name(name), m_Age(age) {}
// 加一个展示方法
void show()
{
cout << "姓名:" << m_Name << "\t年龄:" << m_Age << endl;
}
};
// 定义一个仿函数
class ComparePerson
{
public:
//在类中重载()运算符
//声明为常成员函数,因为set禁止修改元素,属于常对象,传参也是常对象
bool operator()(const Person &p1, const Person &p2) const
{
return p1.m_Age > p2.m_Age;
}
};
int main()
{
// 如果是自定义数据类型,那必须要指定排序规则,不然无法插入
set<Person, ComparePerson> st;
Person p1("aaa", 10);
Person p2("bbb", 15);
Person p3("ccc", 20);
st.insert(p1);
st.insert(p2);
st.insert(p3);
for (auto ele : st)
{
ele.show();
}
return 0;
}