STL常用容器- set/ multiset 容器

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;
}

  • 0
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值