【STL】C++标准模版库——set

01、目录

02、浅谈set

C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在STL使用过程中,并不会感到陌生。

关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。

03、关于set的几个思考

3.1 为何map和set的插入删除效率比用其他序列容器高?

大部分人说,很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。说对了,确实如此。set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。结构图可能如下:

在这里插入图片描述
请大家忽略图片的美丑,毕竟不是学美术的。哈哈哈哈
因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点也OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。

3.2 为何每次insert之后,以前保存的iterator不会失效?

iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator。

3.3 当数据元素增多时,set的插入和搜索速度变化如何?

如果你知道log2的关系你应该就彻底了解这个答案。在set中查找是使用二分查找,也就是说,如果有16个元素,最多需要比较4次就能找到结果,有32个元素,最多比较5次。那么有10000个呢?最多比较的次数为log10000,最多为14次,如果是20000个元素呢?最多不过15次。看见了吧,当数据量增大一倍的时候,搜索次数只不过多了1次,多了1/14的搜索时间而已。你明白这个道理后,就可以安心往里面放入元素了。

04、set成员方法表

集合(Set)是一种包含已排序对象的关联容器

函数(Function)描述(Description)
begin()返回指向第一个元素的迭代器
end()返回指向最后一个元素的迭代器
clear()清除所有元素
empty()如果集合为空,返回true
count()返回某个值元素的个数
erase()删除集合中的元素
equal_range()返回集合中与给定值相等的上下限的两个迭代器
find()返回一个指向被查找到元素的迭代器
get_allocator()返回集合的分配器
insert()在集合中插入元素
lower_bound()返回指向大于(或等于)某值的第一个元素的迭代器
key_comp()返回一个用于元素间值比较的函数
max_size()返回集合能容纳的元素的最大限值
swap()交换两个集合变量
rbegin()返回指向集合中最后一个元素的反向迭代器
rend()返回指向集合中第一个元素的反向迭代器
size()集合中元素的数目
upper_bound()返回大于某个值元素的迭代器
value_comp()返回一个用于比较元素间的值的函数

05、set成员方法详细用法

5.1 begin() 与 end()
#include <set> //包含set容器的头文件,写一次,后面示例省略

void test()
{
	//一般用于for循环用迭代器遍历容器时。
	set<int> set;
	set.begin(); //返回容器开始迭代器
	set.end(); //返回容器结束迭代器
}

begin():返回指向当前集合中第一个元素的迭代器。
end():返回指向当前集合中最后一个元素的迭代器。

5.2 clear()
void test()
{
	set<int> set;
	set.insert(1);
	set.insert(2);

	for(int i : set)
	{
		cout << i << endl;
	}

	//使用clear()后:
	set.clear();
	for(int i : set)
	{
		cout << i << endl; //已经没有元素了
	}
}

clear():清除当前集合中的所有元素。

5.3 empty()
void test()
{
	set<int> set;
	set.insert(1); //插入元素
	
	if(set.empty()) {
		cout << "该容器内没有元素,为NULL" << endl; //执行此语句
	}
	else{
		cout << "该容器内有元素" << endl;
	}
}

empty():如果当前集合为空,返回true;否则返回false。

5.4 count()
void test()
{
	set<int> set;
	set.insert(1);
	set.insert(2);
	set.insert(3);
	set.insert(2);

	int a = set.count();
	cout << a << endl;
}

count():用来查找set中某个某个键值出现的次数。这个函数在set并不是很实用,因为一个键值在set只可能出现0或1次,这样就变成了判断某一键值是否在set出现过了。

5.5 erase()
/*
void erase( iterator i );
void erase( iterator start, iterator end );
size_type erase( const key_type &key );
*/

void test()
{
	set<int> set;
	set.insert(1);
	set.insert(3);
	set.insert(1);

	int a = set.erase(1);
	cout << "删除1的个数是:" << a << endl;
	
	set.erase();
	if(set.empty()){
		cout << "NULL" << endl;
	}
	else{
		cout << "NOT NULL" << endl;
	}
}
  • 删除i元素
  • 删除从start开始到end结束的元素
  • 删除等于key值的所有元素(返回被删除的元素的个数)

小结:set中的删除操作是不进行任何的错误检查的,比如定位器的是否合法等等,所以用的时候自己一定要注意。

5.6 equal_range()
//pair equal_range( const key_type &key );

void test()
{
	set<int> se;
	se.insert(10);
	se.insert(20);
	se.insert(30);
	se.insert(40);
	se.insert(50);
	//注意,这里为了避免编译器歧义,set<int> 名字,不要跟set一样,否则会产生歧义
	pair<set<int>::const_iterator , set<int>::const_iterator> pr = se.equal_range(20);
}

equal_range() ,返回一对定位器,分别表示第一个大于或等于给定关键值的元素和 第一个大于给定关键值的元素,这个返回值是一个pair类型,如果这一对定位器中哪个返回失败,就会等于end()的值。具体这个有什么用途我还没遇到过~~~

5.7 find()
//iterator find( const key_type &key );

void test()
{
	set<int> se;
	se.insert(10);
	se.insert(20);
	se.insert(30);

	set<int>::const_iterator it = se.find(20);
	cout << *it << endl;
}

在当前集合中查找等于key值的元素,并返回指向该元素的迭代器;如果没有找到,返回指向集合最后一个元素的迭代器。

5.8 get_allocator()
//allocator_type get_allocator();

void test()
{
	set<int> se;
	se.insert(1);
	se.insert(10);

	printf("分配器:%d\n",se.get_allocator()); //204
}

返回当前集合的分配器,基本不用此函数

5.9 insert()
/*
iterator insert( iterator i, const TYPE &val );
void insert( input_iterator start, input_iterator end );
pair insert( const TYPE &val );
*/

void test()
{
    int a[] = {1,2,3};  
    set<int> s;  
    set<int>::iterator iter;  
    s.insert(a,a+3);  
    for(iter = s.begin() ; iter != s.end() ; ++iter)  
    {  
        cout<<*iter<<" ";  
    }  
    cout<<endl;  
    pair<set<int>::iterator,bool> pr;  
    pr = s.insert(5);  
    if(pr.second)  
    {  
        cout<<*pr.first<<endl;  
    }  
    return 0;  
}

  • 在迭代器i前插入val
  • 将迭代器start开始到end结束返回内的元素插入到集合中
  • 在当前集合中插入val元素,并返回指向该元素的迭代器和一个布尔值来说明val是否成功的被插入了。(应该注意的是在集合(Sets)中不能插入两个相同的元素。)
5.10 size()
//size_type size();

void test()
{
	set<int> se;
	se.insert(10);
	se.insert(20);

	size_t a = se.size();
	cout << a << endl;
}

size():返回当前集合中元素的数目。

5.11 swap()
//void swap( set &object );

void test()
{
	set<int> a;
	a.insert(1);
	set<int> b;
	b.insert(20);

	a.swap(b);
	for(int i : a)
	{
		cout << i << endl;
	}
}

swap():交换当前集合和object集合中的元素。

5.12 lower_bound() 与 upper_bound()
void test()
{
    set<int> s;  
    s.insert(1);  
    s.insert(3);  
    s.insert(4);  
    cout<<*s.lower_bound(2)<<endl;  
    cout<<*s.lower_bound(3)<<endl;  
    cout<<*s.upper_bound(3)<<endl; 
}

lower_bound(key_value) ,返回第一个大于等于key_value的定位器

upper_bound(key_value),返回最后一个大于等于key_value的定位器

5.13 rbegin() 与 rend()
reverse_iterator rbegin(); //返回指向当前集合中最后一个元素的反向迭代器
reverse_iterator rend(); //返回指向集合中第一个元素的反向迭代器

这个就是跟begin() 与 end() 相反的,这里不重复。

5.14 max_size()
size_type max_size(); //返回当前集合能容纳元素的最大限值

void test()
{
	set<int> se;
	size_t Maxlen = se.max_size();
	cout << "当前能容纳的最大个数是:" << Maxlen << endl;
}
5.15 key_comp() 与 value_comp()
key_compare key_comp(); //返回一个用于元素间值比较的函数对象
value_compare value_comp(); //返回一个用于比较元素间的值的函数对象

此函数用的很少,我基本没有用过,感兴趣可以研究下。

06、总结

本篇就暂时聊这点吧,set容器的基本的上面都有讲解。
STL专栏的内容,重在实践,暂时的话就说浅显一点。后面应该还会出一刊《STL源码剖析》一书中的部分内容,到时候没有说到的,我们在深入讨论。

版权声明:转载请注明出处,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cain Xcy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值