C++-set的基本概念和接口

###前言:(序列式容器和关联式容器)

前面我们已经学习的部分的STL的容器,例如string、list、vecto、deque、array、forward_list。这些都是序列式容器,逻辑结构为线性序列,两个数据之间没用很强的关联性;

关联式容器 也是来存储数据的,它的逻辑结构是非线性序列的 ,两个数据之间的关联性很强 ,交换一下或者改变一下,那么他们之间的存储结构就被破坏了;关联式容器有map/set系列的和unordered_map和unordered_map系列的。

一、set

1、set类的介绍

set是存储数据的容器,它的存储数据的规则按照红黑树的规则;左子树的节点数据都小于根节点的数据,右子树的节点数据都大于根节点的数据大小;

声明:

cpp 复制代码
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;
  • T是set底层的关键字类型;
  • set默认支持的是小于的比较,less<T>,可以传greater<T>;
  • set底层存储的数据的内存是从空间适配器中申请的,可以自己实现内存池,那么就传给第三个参数;
  • 一般来说,我们不需要传后面的两个参数
  • 底层是红黑树,增删查的时间复杂度是O(logN);
  • 底层迭代器走的是平衡二叉搜索树的中序遍历,所以迭代器对应的数据是有序的,

2、set的构造和迭代器

set的迭代器是双向的迭代器,所以支持反向迭代器;

迭代器走的是中序,所以默认遍历按照升序顺序;

set的iterator和const_iterator都不错支持修改数据,因为修改数据可能会破坏底层搜索树的结构

###代码示例:

cpp 复制代码
#include<iostream>
#include<set>
using namespace std;

void Print(set<int>& s)
{
	for (auto i : s)
	{
		cout << i << " ";
	}
	cout << endl;
}
void test1()
{
	//无参构造
	set<int> s1;
	Print(s1);
	//迭代器区间构造
	int arr[] = { 5,6,7,1,2,3,4 };
	set<int> s2(arr, arr + 7);
	set<int> s3(s2.begin(), s2.end());
	Print(s2);
	Print(s3);
	//拷贝构造
	set<int> s4(s3);
	Print(s4);
	//列表构造
	set<int> s5({ 9,1,3,4,5,7,6,8,2 });
	Print(s5);
}
cpp 复制代码
void test2()
{
	set<int> s({1,2,3,4,9,8,7,6,5});
	//正向迭代器
	set<int>::iterator i = s.begin();
	while (i != s.end())
	{
		cout << *i << " ";
		i++;
	}
	cout << endl;
	//反向迭代器
	set<int> ::reverse_iterator ri = s.rbegin();
	while (ri != s.rend())
	{
		cout << *ri << " ";
		ri++;
	}
	cout << endl;
}

3、set的增删查

1、插入insert

set不支持插入相同的值,若是插入相同的值,那么就插入失败;

插入接口原型声明:

// 单个数据插⼊,如果已经存在则插⼊失败
pair<iterator,bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);

###代码示例:

cpp 复制代码
void test3()//插入单个值
{
	set<int> s1;
	s1.insert(2);
	s1.insert(3);
	s1.insert(8);
	s1.insert(1);

	for (auto i : s1)
	{
		cout << i << ' ';
	}
	cout << endl;
	//相同元素不插入
	pair<set<int>::iterator,bool> ret =s1.insert(8);
	for (auto i : s1)
	{
		cout << i << ' ';
	}
	cout << endl;
}
cpp 复制代码
void test4()//插入一段列表
{
	set<int> s;
	for (int i = 10; i <= 100; i += 10)
	{
		s.insert(i);
	}
	for (auto i : s)
	{
		cout << i << ' ';
	}
	cout << endl;
	s.insert({ 300,200,100,10 });
	for (auto i : s)
	{
		cout << i << ' ';
	}
	cout << endl;
}

2、查找find

原型:

iterator find(const value_type& val)const;

若是查找成功则返回这个数据对应的迭代器;若是查找失败则返回end迭代器。

###代码示例:

cpp 复制代码
void test5()
{
	set<int> s;
	for (int i = 10; i <= 100; i += 10)
	{
		s.insert(i);
	}
	for (auto i : s)
	{
		cout << i << ' ';
	}
	cout << endl;
	//查找一个存在的值
	set<int>::iterator pos1 = s.find(10);
	if (pos1 != s.end())
		cout << *pos1 << endl;
	else
		cout << "该数据不存在" << endl;
	//查找一个不存在的值
	set<int>::iterator pos2 = s.find(101);
	if (pos2 != s.end())
		cout << *pos2 << endl;
	else
		cout << "该数据不存在" << endl;

3、删除erase

原型:

1、void erase(iterator pos);

2、size_type erase(const value_type& val);

3、void erase(iterator first,iterator last);

  • 第一个就是删除 迭代器指向位置的值;
  • 第二个删除指定的值,返回类型size_type代表的是删除了多少个这个指定的值,set没有相同的值,那么就是删除成功返回1,反之返回0;
  • 第三个删除指定迭代器区间的数据。

###代码示例:

cpp 复制代码
void test6()
{
	set<int> s = { 4,2,7,2,8,5,9 };
	cout << "原set数据:" << endl;
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	// 删除最⼩值
	cout << "删除最小值:" << endl;
	s.erase(s.begin());//迭代按中序走,开始的就是最小的值
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << "删除指定值x,输入x:" << endl;
	// 直接删除x
	int x;
	cin >> x;
	int num = s.erase(x);//指定一个值删除
	if (num == 0)
	{
		cout << x << "不存在!" << endl;
	}
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << "删除迭代器区间的值:" << endl;
	set<int> ::iterator first = s.begin();
	set<int> ::iterator last = s.end();
	s.erase(first, --last);//结果只剩下最后一个数据
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

4、set的lower_bound和upper_bound

原型

iterator lower_bound(const value_type& val)const;

iterator upper_bound(const value_type& val)const;

  • 第一个是返回大于或者等于val值的迭代器;
  • 第二个时返回大于val值的迭代器。

###代码示例:

cpp 复制代码
void test7()
{
	set<int> s;
	for (int i = 10; i <= 100; i += 10)
	{
		s.insert(i);
	}
	for (auto i : s)
	{
		cout << i << ' ';
	}
	cout << endl;
	set<int>::iterator it_low = s.lower_bound(20);
	cout << *it_low << endl;
	
	set<int>::iterator it_up = s.upper_bound(50);
	cout << *it_up << endl;

	//删除掉这两个迭代器之间的数据,左闭右开
	s.erase(it_low, it_up);
	for (auto i : s)
	{
		cout << i << ' ';
	}
}

二、multiset

multiset和set几乎完全一致,只是multiset支持冗余,也就是支持存储相同的值。所以它的增删查也会随之发生小变化;

头文件和set相同

  • insert:能插入相同的值;
  • find:查找到要找的值,那就去它的左子树看看有无相同的值,若是有就返回左子树的节点迭代器,只到左子树中没有这个值为止;也就是中序中的第一个;
  • erase:指定删除一个值,那么也会删除所有与之相同的值。

###具体代码:

cpp 复制代码
void test8()
{
	multiset<int> s1{ 4,2,7,2,4,8,4,5,4,9 };
	for (auto i : s1)
	{
		cout << i << " ";
	}
	cout << endl;

	cout << "输入需要查找的值:" << endl;
	int x = 0;	cin >> x;
	multiset<int>::iterator i = s1.find(x);
	if (i != s1.end())
		cout <<"这个值存在:" << *i << endl;
	else
		cout << "该数据不存在" << endl;

	//与set不同,multiset的count会返回实际个数
	cout <<"数据个数:" << s1.count(4) << endl;

	//erase会删除所有相同的值
	s1.erase(4);
	auto j = s1.begin();
	while (j != s1.end())
	{
		cout << *j << " ";
		j++;
	}
}
相关推荐
湫ccc1 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe1 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin1 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python
Ysjt | 深2 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__2 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
码农飞飞2 小时前
深入理解Rust的模式匹配
开发语言·后端·rust·模式匹配·解构·结构体和枚举
一个小坑货2 小时前
Rust 的简介
开发语言·后端·rust
湫ccc2 小时前
《Python基础》之基本数据类型
开发语言·python