【STL】——关联容器

1、关联容器

1.1基本概念

关联容器也是存储数据的容器,但是和顺序容器很大的区别。
1、关联容器底层数据结构
底层存放红黑树

  • BST:二叉搜索树、二叉排序树:保证中序遍历排序升序有序。所以在插入结点时,需要调整结点
  • 红黑树:是一种特殊的BST树,把结点划分为红节点和黑节点,进行调整

2、关联容器分类

  • set:单集合容器
  • multiset:多集合容器
  • map:单映射容器
  • multimap:多重映射容器

1.2关联容器都有的函数

1、构造函数

  • 默认的构造函数:直接开辟空间,如set s
  • 传入迭代器(指针)区间的构造函数:传入起始位置,末尾位置,会把起始~末尾的迭代器区间元素传入容器。注意也可以传入数组,字符串的指针。如set s(arr,arr+len),将arr~arr+len区间的数据传入容器。注意map的数组不是一维数组,所以尽量不使用区间构造map。

2、其他函数

函数名称 含义 使用
find查找函数 因为关联容器底层是红黑树,中序遍历有序,可以使用二分查找,所以关联容器实现了自己的find,利用二分查找实现元素,时间复杂度是O(logn),比泛型算法find时间复杂度O(n)速度快 setiterator fit=s.find(x);//在s容器中查找元素x
count计数函数 返回关联容器中值为x的元素个数 int co=s.count(x);//返回x的个数
清空函数 清空容器元素 c.clear()

3、类型别名

类型别名 含义
key_value 容器的关键字类型
mapped_type 每个关键字关联的类型,只适用于map
value_type 对于set,与key_type相同;对于map,等同于pair,mapped_type>

迭代器需要进行自加操作,用到it++,++it

  • it++:先使用旧值运算,再++;原理:先对a进行备份,产生临时量,内置类型的临时量是常量,整个运算过程中用的都是临时量,直到表达式结束时,把a自加
  • ++it:先自加++后,再运算。原理:用it本身进行自加,返回it本身

所以it=begin(),it++后,还是指向begin()位置;++it后向前走了一个,指向第二个位置。

2、set 详解

set单集合,存放元素value,不允许重复,不允许修改,按照一定次序存储,头文件为:

# include<set>

2.1基本操作

1、增
首先我们定义一个单重集合。他存储key值不会重复的元素

set<int> set1;

然后循环插入元素

for (int i = 0; i < 50; i++)
	{
		set1.insert(rand() % 20 + 1);
	}

对于关联容器的insert只需要给入插入元素的值即可,不需要像vector/deque/list还要给出迭代器。因为其底层是哈希表对于该插入的位置有相应的函数计算
没有必要提供端点位置的数据插入(前插、尾插),因为尾插后,会将其调整,调整后不一定在最后,所以只需要提供任意位置插入函数即可。
插入时不需要传入位置,只需要传入数值即可,因为树形结构会进行调整。
插入方式:

插入方式 形式 含义
插入元素 insert(val) 将元素val插入set,插入后会自动调整排序
给指定位置插入元素 insert(index,val) 位置无效,只是为了和前面顺序容器兼容,你传入了位置,不一样插入指定的位置,因为存在自动调整
插入区间元素 insert(first,last) 在set中插入(first,last)区间的元素

2、查
方式一:采用迭代器遍历

auto it1 = set1.begin();
	for (; it1 != set1.end(); ++it1)
	{
		cout << *it1 << " ";
	}

方式二:采用for each遍历

for (int v : set1)
	{
		cout << v << " ";
	}

3、删除
方式一:用迭代器遍历去找元素,找到了删除

for (it1 = set1.begin(); it1 != set1.end(); )
	{
		if (*it1 == 30)
		{
			it1 = set1.erase(it1);//调用erase,it1迭代器就失效了,所以要对迭代器更新
		}
		else
		{
			++it1;
		}
	}

方式二:使用函数find
find如果找不到元素会返回end迭代器

int main()
{
	int arr[] = { 1,12,54,6,5,5 };
	int len = sizeof(arr) / sizeof(arr[0]);
	std::set<int> myset(arr, arr + len);
	std::set<int> ::iterator it1 = std::find(myset.begin(),
		myset.end(),30);
	if (it1 != myset.end())
	{
		myset.erase(it1);
	}
	else
	{
		std::cout << "not find!" << std::endl;
	}
}


删除方式 形式 含义
删除区间元素 erase(first,last) 删除first~last之间的元素
删除指定位置的元素 erase(index) 删除set上index上的元素
删除元素 erase(val) 删除元素val

4、其他操作
我们来查看此容器的大小和key为15的元素个数,具体操作如下:

for (int i = 0; i < 50; i++)
	{
		set1.insert(rand() % 20 + 1);
	}
cout << set1.size() << endl;
cout << set1.count(15) << endl;

结果如下:
在这里插入图片描述
虽然我们输入了50个数,因为是单重集合,所以其自动去除了重复的元素,且key为15的元素个数肯定也是1.

但是同样是上述代码的插入,如果我们将其改成了多重集合,定义如下:

multiset<int> set1;

输出结果如下:
在这里插入图片描述
因为其存储key值是会重复的元素。所以50个元素全部都存储进去了,且key为15的元素个数就是相应重复的个数了。

2.2特点

  • 不允许重复,即key关键字不允许重复,可以去重
  • set元素不能在容器中修改,因为:元素用const修饰,修改过后不能自己调整红黑树。导致序列乱序。所以修改一个元素可以先删除再插入。
  • set在插入元素时,会进行排序,默认按照红黑树中序遍历升序的方式排序, 所以 如果用set存储对象信息,需要在对象对应的类中提供比较方式,因为set在存储数据时,不知道怎么比较对象。
  • 基于关键字的快速查询。

应用一
题目要求:在10万整数中,在海量数据中查找重复的元素
分析:因为在此要求我们在海量数据中只是找到重复的元素值即可,所以采用set是一个非常好的方法,具体代码实现如下:

int main()
{
   
	const int ARR_LEN = 100000;
	int arr[ARR_LEN] = {
    0 };
	for (int i = 0; i < ARR_LEN; ++i)
	{
   
		arr[i] = rand() % 10000 + 1;
	}
	unordered_set<int> set;
	for (int v : arr)//O(n)
	{
   
		set.insert(v);//O(1)
	}
	for (int v : set)
	{
   
		cout << v << " ";
	}
	cout << endl;
	return 0;
}

应用二
set的简单使用

# include<iostream>
# include<deque>
# include<vector>
# include<list>
# include<algorithm>
# include<iterator>
# include<set>

template<typename Container>
void show(Container& con)
{
   
	typename Container::iterator it = con.begin();
	for (; it != con.end(); it++)
	{
   
		std::cout << *it << " ";
	}
	std::cout << std::endl;
}

int main()
{
   
	std::set<int> myset1;//无参构造
	int a[] = {
    0,1,2,3,4,5,6,7 };
	int len = sizeof(a) / sizeof(a[0]);
	std::set<int> myset(a, a + len);//通过迭代器区间构造

	//2.迭代器打印整型容器
	show(myset);

	//3.迭代器指向起始位置,将数组的元素插入2号下标,会去重,所以不会插入
	myset.insert(a + 2, a + 5);
	show(myset)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值