目录
二分查找 lower_bound()与 upper_bound()
迭代器的访问
set只能通过迭代器(iterator)访问:
set<int>::iterator it;
set<char>::iterator it;
这样我们就得到了迭代器 it ,并且我们可以通过 *it 的方式来访问 set 里的元素
一定注意!!!!!!不能像vector一样用 *(it + x) 的方式访问,但是我们可以用循环的方式访问进行访问:
set<int>st = { 1,2,5,6,8,12 };
//一定要注意,不支持 it < st.end() 的写法,所以我们采用it != st.end()的写法
for (set<int>::iterator it = st.begin(); it != st.end(); it++)
cout << *it << ' ';
当然,因为auto具有自动推断变量类型,所以也可以这么写(此文之后都用auto)
set<int>st = { 1,2,5,6,8,12 };
//这里的 it 被 auto 自动推断为 set<int>::iterator 类型
for (auto it = st.begin(); it != st.end(); it++)
cout << *it << ' ';
迭代器的移动
迭代器的 ++ 或 -- 操作
C++ STL 标准库为 set 容器配置的迭代器类型为双向迭代器。这意味着,假设 p 为此类型的迭代器,则其只能进行 ++p、p++、--p、p--、*p 操作,并且 2 个双向迭代器之间做比较,也只能使用 == 或者 != 运算符。
但是请注意!!!p += 2,p -= 5 这样的操作是不被允许的!!!!
set<int>::iterator it;
set<int>st = { 1,2,5,6,8,12 };
it = st.begin();
cout << *it << '\n'; // 1
it++;
cout << *it << '\n'; // 2
it++;
cout << *it << '\n'; // 5
it--;
cout << *it << '\n'; // 2
advance()
形如:advance( it , n ) 可以将指定迭代器 it 前移或后移 n 个位置的距离(正数右移,负数左移),具体用法如下:
set<int>::iterator it;
set<int>st = { 1,2,5,6,8,12 };
it = st.begin();
cout << *it << '\n'; // 1
advance(it, 2);
cout << *it << '\n'; // 5
advance(it, -1);
cout << *it << '\n'; // 2
其实,对于set类型的迭代器,advance() 函数底层是通过重复执行 n 个 ++ 或者 -- 操作实现的。
我们需要注意的是,我们需要保证移动距离 n 应当在合适的范围内
通过迭代器查找
next( ) 或 prev( )
next( it , n ) :当 n 为正数时,其返回的迭代器将位于 it 右侧;反之,当 n 为负数时,其返回的迭代器位于 it 左侧。若不写,则默认为 n = 1 , 即右侧一个位置
prev( it , n ) :当 n 为正数时,其返回的迭代器将位于 it 左侧;反之,当 n 为负数时,其返回的迭代器位于 it 右侧。若不写,则默认为 n = 1 , 即左侧一个位置
一定注意!!!这两个函数并不会让原迭代器 it 移动,只是通过 it 去查询相应位置的新迭代器的值!!!
如要查找迭代器 it 后面 n 位置的元素:
next( it , n ) 或者 pre( it , -n )
如要查找迭代器 it 前面 n 位置的元素:
next( it , -n ) 或者 pre( it , n )
由此可见,next()和 prev()正好相反
特别的,我们需要注意n的大小以及指向的位置,因为 next() 和 prev() 自身不会检查新迭代器指向的有效性,需要我们自己来保证。
具体用法如下:
set<int>::iterator it;
set<int>st = { 1,2,5,6,8,12 };
it = st.begin();
it++; it++;
cout << *it << '\n'; // 5
cout << *next(it, 2) << '\n'; // 8
cout << *prev(it, -1) << '\n'; // 6
cout << *prev(it) << '\n'; // 2
find()
find( value ) 的形式可以返回 set 中对应值得迭代器,时间复杂度为 O( logN ),N 为 set 内得元素个数。
特别的,如果set中没有这个元素,则返回 st.end() 的迭代器
set<int>::iterator it;
set<int>st = { 1,2,5,6,8,12 };
it = st.find(8);
cout << *it << '\n'; // 8
it = st.find(15); //此时相当于it被赋值为st.end()
it--;
cout << *it << '\n'; // 12 这里可以证明上面find返回的是st.end()
二分查找 lower_bound()与 upper_bound()
一定注意!!!!!!!
虽然 algorithm 库里面有 lower_bound() 和 upper_bound() 函数,set类型数据也可以强行调用,结果一样,但是时间复杂度是 O(n) 的
而用 set 内部自带的函数 lower_bound() 和 upper_bound() 函数 时间复杂度才是 O(logn) 的!!!
set<int>s = { 1,3,6,9,12 };
int temp1, temp2, temp3, temp4;
temp1 = *lower_bound(s.begin(), s.end(), 4);//时间复杂度 O(n)
temp2 = *upper_bound(s.begin(), s.end(), 4);//时间复杂度 O(n)
temp3 = *s.lower_bound(4);//时间复杂度 O(logn)
temp4 = *s.upper_bound(4);//时间复杂度 O(logn)
cout << temp1 << '\n';// 6
cout << temp2 << '\n';// 6
cout << temp3 << '\n';// 6
cout << temp4 << '\n';// 6
set的删改
erase()
三种方式进行删除 set<int>s 中的元素
①s.erase( value )
时间复杂度是 O( logN ) ,N为set内存放的元素个数
此函数的返回值是成功删除的个数
value为需要删除的数值,类型应为set中存放的类型(此处例子为int)
若s中没有value的值,则s不变
set<int>s = { 1,3,6,9,12 };
int num;
num = s.erase(9);// 删除后s内为 1 3 6 12 , num 为 1
②s.erase( it )
时间复杂度是 O( 1 )
此函数的返回的是成功删除的迭代器的下一个迭代器
it 是所需要删除的元素的迭代器( it 不可以为 s.end() 不然再删除过程中会报错!),与此同时,我们也可以结合 set 中的 find() 函数来组合使用,因为 find() 函数返回的是迭代器。
set<int>s = { 1,3,6,9,12 };
set<int>::iterator it;
it = s.erase(s.begin());// 删除后为 3 6 9 12 , *it 此时为 3
s.erase(s.find(6));// 删除后为 3 9 12
③s.erase( first , last )
时间复杂度是 O( last - first )
此函数的返回的是成功删除最后一个迭代器的下一个迭代器,也就是 last
这里 first 是需要删除区间的起始迭代器,而 last 是需要删除的区间的末尾迭代器的下一个迭代器,从而实现删除 [ fist , last ) 区间的元素。
set<int>s = { 1,3,6,9,12 };
set<int>::iterator it;
it = s.erase(++s.begin(), --s.end());//删除后s内为 1 12 , *it 为 12
cout << *it << '\n'; // 12
insert()与 emplace()
简单来说,在 vector 或 list 中用 emplace() 效率可能会比 insert() 高些,但在 map 或 set 这种容器来说,insert() 更加适用
详细情况,可以参照此处对于 map 的 insert() 函数与 emplace() 函数的分析深入了解两函数
C++ Set emplace vs insert when an object is already created - Stack Overflow
C++ STL set emplace()和emplace_hint()方法详解 (biancheng.net)
set的其他常见函数及其使用
待更新...