欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > STL标准库

STL标准库

2025/3/16 15:38:48 来源:https://blog.csdn.net/m0_74640012/article/details/146267769  浏览:    关键词:STL标准库

感谢哔哩哔哩UP“开发者LaoJ”,以下是学习记录~

一、容器

1.1、vector

底层实现是动态数组,向尾部插入数据很方便,但是向中间和头部插入数据需要移动其它元素

可以实现随机访问

如果插入时,当前vector容纳不下,会申请一个更大的空间,然后将当前vector拷贝过去,在执行插入操作

vector<T> v1;                     //默认初始化,v1不包含任何元素
vector<T> v2(v1);
vector<T> v2 = v1;
vector<T> v3(n, val);             //n个元素均为val
vector<T> v4(n);                  //包含n个重复执行了值初始化的对象
vector<T> v5{a, b, c, …};
vector<T> v5 = {a, b, c, …};/*关于vector容器的操作*/
vector<int> ivec;
//1、插入元素
ivec.push_back(10);                   //在末尾追加元素10,涉及数据拷贝
ivec.emplace_back(10);                //在末尾追加元素10,不涉及拷贝,直接在容器中创建元素
ivec.insert(ivec.begin() + 2, 10);    //在vector中的第2个位置插入10//2、删除元素
ivec.pop_back();                      //删除末尾元素
ivec.erase(ivec.begin() + 2);         //将vector中的第2个位置元素删除//3、获取迭代器
auto beg = ivec.begin();              //获取指向vector首元素的迭代器
auto end = ivec.end();                //获取指向vector尾元素的下一位置的迭代器//4、关于容器大小
ivec.size();                          //得到ivec的大小
ivec.capacity();                      //得到目前ivec的容量
ivec.resize();                        //修改ivec的大小(删/补默认值)//5、访问vector
ivec[0];                              //获取ivec的第0个元素
ivec.at(0);                           //获取ivec的第0个元素
for (auto tmp : ivec) {……}            //通过范围for来遍历ivec/*当vector使用const修饰时*迭代器会变为指向常量的迭代器*因此,不可以使用解引用迭代器来修改vector中的内容*/

1.2、deque

底层实现是一系列固定大小的数据块(分段连续),插入操作方便,可以实现随机访问

双向开口,即两端均可插入和删除数据

#include<iostream>
#include<vector>
#include<deque>
#include <algorithm>
using namespace std;int main(void)
{//创建队列deque<int> mydeq1(5, 10);deque<int> mydeq2 = { 5, 4, 3, 2, 1 };deque<int> mydeq3(mydeq2.begin(), mydeq2.end());vector<int> vec = { 1, 2, 3, 4, 5 };deque<int> mydeq4(vec.begin(), vec.end());//插入mydeq2.push_back(6);mydeq2.emplace_back(7);mydeq2.push_front(0);mydeq2.insert(mydeq2.begin() + 2, 0);//删除mydeq2.pop_front();mydeq2.pop_back();mydeq2.erase(mydeq2.end() - 3);//排序,sort是原地排序sort(mydeq2.begin(), mydeq2.end());//成员访问cout << mydeq2.at(2) << endl;cout << mydeq2.front() << endl;cout << mydeq2.back() <<endl;//其它函数cout << mydeq2.size() << endl;mydeq2.resize(5);cout << mydeq2.size() << endl;mydeq2.clear();swap(mydeq1, mydeq2);return 0;
}

1.3、list

底层实现是双向链表,支持双向迭代,可以高效的插入和删除数据(修改指针即可),内存不连续

但是不支持随机访问(不可以对迭代器进行加减,但是可以使用advanc移动迭代器),访问元素需要O(n)复杂度

#include<list>
#include<vector>
#include<iostream>
using namespace std;int main(void)
{/*创建list*/list<int> mylist1 = {5, 4, 4, 4, 4, 3, 2, 1};list<int> mylist2(5, 10);vector<int> vec = { 6, 7, 8, 9, 10 };list<int> mylist3(vec.begin(), vec.end());list<int> mylist4 = mylist3;list<int> mylist5(mylist4);list<int> mylist6(move(mylist5));          //此时,mylist5为空,其中的元素被“搬运”到了mylist6list<int> mylist7, mylist8;mylist7.assign(3, 8);mylist8.assign(vec.begin(), vec.end());//获取迭代器auto beg = mylist1.begin(), end = mylist1.end();cout << "*beg:" << *beg << endl;//插入元素mylist1.push_back(6);mylist1.push_front(0);mylist1.insert(end, 9);//删除元素mylist1.pop_back();mylist1.pop_front();mylist1.erase(beg);mylist1.unique();//排序,默认升序mylist1.sort();/*实现降序排序:*myList.sort([](int a, int b) {*   return a > b;  // 降序排序*});*///splice/*advance可以移动迭代器,而next(it, 3)只是临时返回it+3处的“迭代器”,并没有修改it*/auto it = mylist1.begin();advance(it, 2);mylist2.splice(mylist2.begin(), mylist1, it, next(it, 3));//其它,splice需要再学cout << mylist1.front() << endl;cout << mylist1.back() << endl;mylist1.merge(mylist2);       //将mylist2插入到mylist1的末尾,完成操作后,mylist2为空mylist1.reverse();            //翻转mylist1return 0;
}

1.4、map

map是关联容器,即存储的是键值对,其中键是唯一的。通常情况下,按照键的升序排列

如果访问不存在的键,如:map_name[key]时,会新创建一个键值对,value的值进行默认初始化

map通常是使用红黑树进行实现的

为什么不使用平衡二叉树而是使用红黑树?

  • 红黑树的平衡要求相对宽松,实现更简单
  • 红黑树的调整策略使得其在插入和删除中表现得更稳定
#include<map>
#include<iostream>
using namespace std;int main(void)
{//创建/*默认情况下,map是按照key的升序进行排列的*如:map<string, int> mymap; 此时,map是按照string的升序排列的*加greater<key_type>后,将按照降序进行排序*如:map<string, int, greater<string>> mymap; 此时,map是按照string的降序排列的*/map<string, int, greater<string>> mymap = {{"zhang", 91},{"li", 78},{"wang", 85}};//插入mymap["zhangsan"] = 90;mymap["lisi"] = 98;mymap["wangwu"] = 88;mymap.insert({ "zhou", 100 });//访问auto val1 = mymap["lisi"];auto val2 = mymap["lisan"];         //并不存在key值为“lisan”的键值对,此时会新建一个键值对,其对应的value值为默认值auto val3 = mymap.at("zhou");cout << val1 << endl;//删除mymap.erase("lisan");mymap.erase("wangrensun");         //试验删除一个不存在的对象,貌似没啥影响,后续再探究一下//查找map<string, int>::iterator it = mymap.find("lisi");if (it != mymap.end())cout << "find it! it's value is:" << it->second << endl;elsecout << "find fail!" << endl;//遍历for (const auto& tmp : mymap)cout << "(" << tmp.first << "," << tmp.second << ")" << endl;for (auto beg = mymap.begin(); beg != mymap.end(); ++beg)cout << "(" << beg->first << "," << beg->second << ")" << endl;//其它mymap.clear();                     //清空mapmymap.size();                      //获得map的大小return 0;
}

1.5、set

set是一种关联容器,用于存储一组唯一的元素,并按照一定的排序规则自动排序

底层实现通常基于红黑树,插入、删除和查找操作的时间复杂度为O(lgn)

#include<iostream>
#include<set>
using namespace std;int main(void)
{/*创建*/set<int> s1;/*键值是唯一的,所以即使初始化列表中有多个1,但是set中只会有一个1*/set<int> s2 = { 3, 1, 4, 1, 5, 9 };set<int> s3(s2);/*插入*不可以对迭代器进行加减,但可以通过advance移动*好像找固定位置插入用处也不大,因为插入后会进行排序~~*/s2.insert(6);auto it = s2.begin();advance(it, 2);s2.insert(it, 8);s2.insert(s2.begin(), 0);/*删除*/s2.erase(0);s2.erase(it);/*查找*/cout << *(s2.lower_bound(5)) << endl;   //第一个大于等于5的cout << *(s2.upper_bound(7)) << endl;   //第一个大于7的auto loc = s2.find(3);if (loc != s2.end())cout << "Find it!" << endl;/*交换*/s1.swap(s2);/*遍历*/for (const auto& tmp : s1)cout << tmp << " ";cout << endl;for (auto beg = s1.begin(); beg != s1.end(); ++beg)cout << *beg << " ";cout << endl;/*其它*/cout << "s1 size:" << s1.size() << endl;s1.clear();cout << "s1 size:" << s1.size() << endl;return 0;
}

1.6、stack

stack是一种适配器,是先进后出的数据结构

不提供迭代器

通常用deque/list作为底层实现

在弹出元素后,试图访问该元素的原始内存位置,会触发未定义行为!不要试图访问已出栈成员

#include<iostream>
#include<stack>
using namespace std;int main(void)
{/*创建*/stack<int> st;/*入栈*/st.push(1);st.push(3);st.push(5);/*访问栈顶*/cout << st.top() << endl;/*出栈*/st.pop();/*判空*/if (st.empty())cout << "stack empty!" << endl;/*获取栈大小*/cout << st.size() << endl;return 0;
}

1.7、queue

queue是一种适配器,是先进先出的数据结构

不提供迭代器

通常用deque/list作为底层实现

此数据结构,可以用于消息队列、任务队列。deque通常用于滑动窗口

#include<iostream>
#include<queue>
using namespace std;int main(void)
{/*创建*/queue<int> qu;queue<int> qu2(qu);    //允许复制构造,不可以有初始化参数列表/*入队*/qu.push(2);qu.push(4);qu.push(6);/*出队*/qu.pop();/*访问队尾/首*/cout << qu.front() << endl;cout << qu.back() << endl;/*判空*/if (qu.empty())cout << "queue empty!" << endl;/*获取大小*/cout << qu.size() << endl;return 0;
}

1.8、unordered_map、unordered_set

这两种容器是基于哈希表的~

元素之间是无序的,键是唯一的

查找速度快,平均时间复杂度为O(1),最坏情况下O(n)

相关操作,unordered_map与map类似,unordered_set与set类似

二、仿函数

仿函数(函数对象)是通过重载operator()的类实例来模拟函数行为的对象。这种特性使得类的对象可以像函数一样被调用

仿函数的优势

  • 仿函数可以保存状态(使用类的成员保存状态,比如保存调用次数)
  • 仿函数是通过对象调用的,编译器可以轻易地将其内联,减少调用开销
  • 可以通过对象的属性来调整其行为
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;class Compare {
public:bool operator()(int a, int b) {cnt++;         //可以记录调用次数return a < b;}
private:int cnt = 0;
};int main() {vector<int> numbers = { 10, 65, 30, 99, 23 };sort(numbers.begin(), numbers.end(), Compare());for (const auto& num : numbers) {cout << num << " ";}cout << endl;return 0;
}

三、算法

传入的操作,一般可以为仿函数、函数、lambda表达式

传入的迭代器,不一定是起始/结束迭代器

一般算法包含在头文件algorithm中

3.1、遍历算法

  • for_each(iterator beg,iterator end,_func);
  • transform(iterator beg1,iterator end1,iterator beg2,_func);

for_each过程中,如果需要对容器中的元素进行修改,_func可以接受引用参数

beg2必须为其对应容器的begin()

transform中的_func接受一个输入,并返回一个值,该值将被写入容器

#include<vector>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;/*传入给for_each*/
class myclass
{
public:void operator()(int& val) {val += 100;}
};
void add100(int& val, int b)
{val += 100;
}/*传入给transform*/
class myclass_t
{
public:int operator()(int val) {return val + 100;}
};
int func(int val)
{return val + 100;
}int main(void)
{vector<int> vec1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };vector<int> vec2 = { 11, 22, 33, 44, 55 };auto Add = bind(add100, placeholders::_1, 0);/*方法一:传入函数,当然,如果函数参数不合适,可以用函数适配器做调整*///for_each(vec1.begin(), vec1.end(), Add);/*方法二:传入lambda表达式*///for_each(vec1.begin(), vec1.end(), [](int& val) {val += 100;});/*方法三:传入仿函数*/for_each(vec1.begin(), vec1.end(), myclass());for_each(vec1.begin(), vec1.end(), [](int val) {cout << val << " ";});cout << endl;//transform(vec1.begin(), vec1.begin() + 5, vec2.begin(), func);//transform(vec1.begin(), vec1.begin() + 5, vec2.begin(), [](int val) {return val + 100;});transform(vec1.begin(), vec1.begin() + 5, vec2.begin(), myclass_t());for_each(vec2.begin(), vec2.end(), [](int val) {cout << val << " ";});cout << endl;return 0;
}

3.2、查找

  • iterator find(iterator beg, iterator end, value)

找到值为value的元素则返回指向它的迭代器,否则返回结束迭代器 

  • iterator find_if(iterator beg,iterator end,_Pred);_Pred是谓词

返回满足_Pred位置对应的迭代器,否则返回结束迭代器

  • iterator adjacent_find(iterator beg,iterator end);

返回相邻元素第一个位置的迭代器,否则返回结束迭代器

  • bool binary_find(iterator beg,iterator end,value);

用于有序序列,找到value值则返回true,否则返回ifalse

  • size_t count(iterator beg,iterator end,value);

返回value出现次数(如果是复杂类型,可能需要重载“==”运算符)

  • size_t count_if(iteraotr beg,iterator end,_Pred);_Pred是统计的条件

返回满足条件的元素个数

3.3、排序

  • sort(iterator beg,iterator end,_Pred);_Pred可以不填,默认为升序排序,实现降序需要传入

对容器内的元素按升序(降序)排序,时间复杂度为O(n*lgn)

  • random_shuffle(iterator beg,iterator end);

将[beg, end]内的元素随机打乱,在调用前需要撒下随机种子srand((unsigned)time(NULL)),srand在头文件ctime中

  • merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator target_beg);

将两个有序的容器合并到另一个目标容器中,合并后该目标容器仍然有序,_beg应为起始迭代器

  • reverse(iterator beg,iterator end);

将容器内的元素进行翻转

3.4、替换

  • swap(container c1,container c2)

交换两个容器内的元素,两个容器类型必须相同

  • replace(iterator beg,iterator end,oldvalue,newvalue)

将[beg, end]范围内的oldvalue元素全部换成newvalue

  • replace_if(iterator beg,iterator end,_Pred,newvalue)

将满足_Pred谓词的元素替换成newvalue

3.5、集合

  • iterator set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator target_beg)

求两个容器内元素的交集,并把这个交集传给另一个容器。返回值是目标容器最后一个元素的下一个地址处的迭代器

  • iterator set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator target_beg)

求两个容器内元素的并集,并把这个并集传给另一个容器。返回值是目标容器最后一个元素的下一个地址处的迭代器

  • set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator target_beg)

求两个容器内元素的差集,并把这个差集传给另一个容器。返回值是目标容器最后一个元素的下一个地址处的迭代器

3.6、算术(#include<numeric>)

  • size_t accumulate(iterator beg,iterator end,value)

返回容器内元素累计总和

  • fill(iterator beg,iterator end,value)

向容器[beg, end]范围填充指定值

四、函数适配器

4.1、bind

可以将函数对象的一些参数绑定为特殊值,从而创建一个新的函数对象

#include<vector>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;bool Isthree(int a, int b) 
{return a == 3;
}int main(void)
{vector<int> vec = { 1, 2, 3, 4, 3, 4, 5, 6, 12, 3, 4, 13, 3, 20 };/*将Isthree中的第二个参数int b,绑定为0*_1表示第一个占位符,后续如果还需要站位符,则_2、_3,以此类推*/auto IsThree = bind(Isthree, placeholders::_1, 0);auto cnt = count_if(vec.begin(), vec.end(), IsThree);cout << "the num of 3 is:" << cnt << endl;return 0;
}

4.2、mem_fn

是一个用于将成员函数转换为可调用对象的适配器。它可以将成员函数包装成一个函数对象,使得成员函数可以像普通函数一样被调用。

#include<vector>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;struct Foo
{void show(int a, int b) {cout << a + b << endl;}
};int main(void)
{Foo foo;/*接受一个成员函数的指针*返回一个可调用对象*/auto showme = mem_fn(&Foo::show);showme(foo, 3, 4);return 0;
}

4.3、not1

not1(谓词取反):它接受一个一元谓词,并返回一个新的谓词,该谓词对原谓词的结果取反

#include<vector>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;int main(void)
{vector<int> vec = { 1, 2, 3, 4, 5 };auto is_even = [](int i) {return i % 2 == 0;};/*C++标准库中的通用函数包装器,用于包装一个接受int类型参数并返回bool类型的可调用对象*用于对一元谓词取反*一元谓词:接受一个参数并返回bool的函数或函数对象*/auto is_odd = not1(function<bool(int)>(is_even));auto res = find_if(vec.begin(), vec.end(), is_odd);if (res != vec.end())cout << "First odd number is: " << *res << endl;return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词