C++ STL set合集(包括set、multiset、unordered_set、unordered_multiset)

前言:

map和set有很多相似的操作,比如其函数部分几乎是一样的,map介绍中还会与本文的一些部分进行比较,文章的排版也和set部分几乎一样,大家可以比较着学,自行食用~ —>C++ STL map合集(包括map、multimap、unordered_map、unordered_multimap)

1.set介绍

C++ STL中标准关联容器set, multiset, 包括map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也称为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,其插入搜索速度都是log级别的,总之效率很高。

set 的含义是集合,它是一个有序的容器,里面的元素都是排序好的,支持插入,删除,查找等操作,就像一个集合一样。所有的操作的都是严格在logn时间之内完成,效率非常高。set 和 multiset 的区别是:set 插入的元素不能相同,但是 multiset 可以相同。

但是unordered_set是用哈希实现的,(其实带unordered都是哈希,un order ed 就是英语无序),它的底层原理是哈希,因此插入是随机的,复杂度O(1)。而删除操作与内部哈希函数有关系,但多数情况是常数级别。

说明:
unordered_set、unordered_multiset头文件:

#include<unordered_set>

set, multiset头文件:

#include <set>	

2.map、set关联式容器总结:

**set和map在实现上有很多相似的地方,具体可以看C++ STL map合集(包括map、multimap、unordered_map、unordered_multimap)

C++STL包含了序列式容器关联式容器
序列式容器里面存储的是元素本身,其底层为线性序列的数据结构。比如:vector,list,deque等。
关联式容器里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高,因为是用树和哈希实现的,因此也不能直接用下标来访问元素。 比如:set、map、unordered_set、unordered_map等。

  • 注意: C++STL当中的stack、queue和priority_queue属于容器适配器,它们默认使用的基础容器分别是deque、deque和vector。
关联式容器容器结构底层实现
set、map、multiset、multimap树型结构平衡搜索树(红黑树)
unordered_set、unordered_map、unordered_multiset、unordered_multimap哈希结构哈希表

一、set

1.定义:
set<Type> s 其实有很多种,只列举一个常用的。
注意:

  • 每个元素的键值都唯一,不允许两个元素有相同的键值。
  • 所有元素都会根据元素的键值自动排序(默认从小到大)。
  • 如果要从大到小排序,需要加入greater< type>: 例如:
    set<int,greater<int> >s;
  • set 中元素的值不能直接被改变。
    2.基本函数:
    这些都是要知道的,其实很多是通用的比如:begin\end\clear\size\empty…
s.begin()					//返回指向第一个元素的迭代器
s.end()						//返回指向最后一个元素的迭代器
s.clear()					//清除所有元素
s.count()					//返回某个值元素的个数
s.empty()					//如果集合为空,返回true,否则返回false
swap()	                    //交换两个集合容器的值
s.erase()					//删除集合中的元素
s.find(k)					//返回一个指向被查找到元素的迭代器
s.insert()					//在集合中插入元素
s.lower_bound(k)			//返回一个迭代器,指向键值大于等于k的第一个元素
s.upper_bound(k)			//返回一个迭代器,指向键值大于k的第一个元素
s.size()					//集合中元素的数目

3.set的遍历
set需要用迭代器遍历或者使用范围基础的 for 循环(C++11 及以上)

#include <iostream>
#include <set>
using namespace std;

int main()
{
    set<int> s;
    s.insert(2);
    s.insert(4);
    s.insert(6);
    s.insert(10); // set 会自动排序
    s.insert(8);

    set<int>::iterator it; // 使用迭代器
    for (it = s.begin(); it != s.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 或者使用范围基础的 for 循环(C++11 及以上)
    for (const auto& element : s) {
        cout << element << " ";
    }
    cout << endl;

    return 0;
}

4.insert函数

s.insert(1);//插入数
s.insert(a[i]);//插入数组

5.erase函数

  • set中的删除操作是不进行任何的错误检查的,比如定位器的是否合法等等,所以用的时候自己一定要注意。
1.erase(iterator) :删除定位器iterator指向的值
2.erase(first,second):删除定位器first和second之间的值
//注意这里的区间是左闭右开的。
//而且要注意是迭代器的值而不是简单的数来表示位置。
3.erase(key_value):删除键值key_value的值

可以自己去编译器里试试这三种情况:

#include <iostream>
#include <set>
using namespace std;
int main(){
     set<int> s;
     set<int>::const_iterator it;
     set<int>::iterator first;
     set<int>::iterator second;
     for(int i=1; i<=10; ++i)
         s.insert(i);
     //第一种删除,删掉1
     s.erase(s.begin());
     //第二种删除,删掉2和3
     first = s.begin();
     second = s.begin();
     second++;
     second++;
     s.erase(first,second);
     //第三种删除,删掉8
     s.erase(8);
     cout<<"删除后 set 中元素是 :";
     for(it=s.begin(); it!=s.end(); ++it)
         cout<<*it<<" ";
     cout<<endl;
     return 0;
}

6.find函数用法:
返回一个指向被查找到元素的迭代器,如果没找到则返回end()

  • 我们只能用来判断set种是否有这个值,却无法知道其具体的位置在哪,没找到返回end()。
//返回一个指向被查找到元素的迭代器,如果没找到则返回end()
#include <iostream>
#include <set>
using namespace std;
int main(){
     set<int> s;
     s.insert(1);
     s.insert(2);
     s.insert(3);
     s.insert(4);
     set<int>::iterator it;
     if((it=s.find(4))!=s.end())
         cout<<*it<<endl;
     return 0;
}

7.lower_bound()、upper_bound() 的用法

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

upper_bound(key_value):返回第一个大于key_value的定位器
  • 一样的我们只能用来判断set大于/等于的这个值是什么,却无法知道其具体的位置在哪。 超过了最大值会返回end();
#include <iostream>
#include <set>
using namespace std;
int main(){
     set<int> s;
     s.insert(1);
     s.insert(3);
     s.insert(4);
     s.insert(6);
     cout<<*s.lower_bound(1)<<endl;
     cout<<*s.lower_bound(2)<<endl;
     cout<<*s.upper_bound(3)<<endl;
     return 0;
}

输出就是1 2 3

set还可以自定义排序,感觉几乎用不着,想了解自己查吧~

二、multiset

multiset容器与set容器的底层实现一样,都是平衡搜索树(红黑树),其次,multiset容器和set容器所提供的成员函数的接口都是基本一致的,这里就不再列举了,multiset容器和set容器的唯一区别就是,multiset允许键值冗余,即multiset容器当中存储的元素是可以重复的。

因此find函数有所不同:

find(val) //返回第一个值为val的元素的迭代器

count在这里才有应用,在普通set里只有0,1,find函数就能代替。

#include <iostream>
#include <set>
using namespace std;

int main()
{
	multiset<int> ms;
	//插入元素(允许重复)
	ms.insert(1);
	ms.insert(4);
	ms.insert(3);
	ms.insert(3);
	ms.insert(2);
	ms.insert(2);
	ms.insert(3);
	for (auto e : ms)
	{
		cout << e << " ";
	}
	cout << endl; //1 2 2 3 3 3 4
	return 0;
}

三、unordered_set

无序集合(unordered_set)是一种使用哈希表实现的无序关联容器,其中键被哈希到哈希表的索引位置,因此插入操作总是随机的。无序集合上的所有操作在平均情况下都具有常数时间复杂度O(1),但在最坏情况下,时间复杂度可以达到线性时间O(n),这取决于内部使用的哈希函数,但实际上它们表现非常出色,通常提供常数时间的查找操作。

函数还是上面的函数,就是少了begin、end等因为是无序的

四、unordered_multiset

大多数操作与 unordered_set 的操作方式相似,但有一些不同之处:

  • 例如,如果某个值v在unordered_multiset中出现了t次,当调用 erase 时,v将被完全删除
    但是,因为 find 函数返回找到值的第一个位置的迭代器,我们可以将此迭代器传递给erase而不是传递实际值,从而仅删除一个副本
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值