C++中STL容器的使用
容器一些基本操作语法
vector
初始化操作
vector<int> a; //声明向量 vector<int> a(10); //声明一个初始大小为10 的向量 vector<int> a(10,1); //初始大小为10,且值都为 1的向量 vector<int> b(a); //声明并用向量a 初始化 向量 b vector<int> b(a.begin(),a.begin()+3); //将 a 向量中从第 0 个 到第二个作为向量 b 的初始值 vector<vector<int>> b(10,vector<int>(5)); //创建一个10*5的二维向量 vector<vector<int> > A(n, vector<int>(n, 0)) // 二维数组,用n个vector进行初始化 n * n二维数组,且初值为0
插入操作
a.insert(a.begin(),1000); //将1000插入到向量a的起始位置前 a.insert(a.begin(),3,1000);//将1000分别插入到向量元素的0-2处(共三个元素) vector<int> a(5,1); vector<int> b(10); b.insert(b.begin(),a.begin(),a.end())//把a.begin和a.end()之间的全部元素插入到 b.begin()前
删除
b.erase(b.begin()); b.erase(b.begin(),b.begin()+3); //删除之间的元素,不包括b.begin()+3
交换
swap(a) //a向量与b向量进行交换
遍历
for(vector<int> :: iterator iter = a.begin();iter!=a.end();iter++){ cout<<*iter<<endl; }
数据存操作
push_back //在数组最后添加一个元素 pop_back //去掉数组最后一个元素 at //得到编号位置的数据 end //得到数组的最后一个单元+1的迭代器 front //得到数组头的引用 back //得到数组最后一个单元的引用 max_size //得到vector最大可以是多大 capacity //当前vector分配的大小(元素个数) size //当前使用数据的大小 resi //改变当前vector所分配空间的大小 clear //清空当前的vector empty //判断vector是否为空 vector<double>values; values.reserve(20);//给values开辟内存空间,使其可以多存入20个元素,但size仍然是0,只有capacity是20 vector<double>values(size,init_value)//创建一个大小为size,初值为init_value的double-vector
反向迭代器
- rebegin():返回一个反向的首元素
- rend():将vector反转的结束指针返回(begin-1);
注意:vector和string 都是顺序容器,在内存中分配了一块连续的存储空间,必须预先为其分配一段空间,这个空间就是capacity,而容器中元素的个数就是size(),在容器中,capacity总是大于等于size;
注意
当出现 size>capacity
时,如果没有空间继续容纳新的元素---要保持存储空间连续
因此容器必须分配新的内存空间,将已有的元素和新的元素拷贝到新的空间,然后释放旧的存储空间
string
头文件<string>
string str;//生成空的字符串 string s(str); //生成一个和str内容相同的字符串 string s(str,idx);//生成一个str[idx]到str末尾内容的字符串 string s(st,idx,length);//生成一个从str[idx]到str[idx+ength]的字符串 string s(c_str);//生成一个接受C风格字符串的string的对象
查询长度和元素
string str; str.size();//返回str的长度 str.length(); //返回str的长度 str.at[idx]; //位于idx索引的字符,越界将会抛出异常 str[idx]; //越界不会抛出异常
比较
string str1("123456"); string str; str.assign(str1); //直接赋值 str.assign(str1,0,3) //把str1[0:3]赋值给str(0,1,2) str.replace(str_pos,length,s); str.replace(str_pos,length,s_idx);
删除
string str1("123456"); str1.erase(str1.begin(),str1.begin()+1); // 使用迭代器删除从开始到开始偏移1个字节的子串 string str1("123"); string str2("321"); str1.swap(str2);// str1和str2交换
增加
string A("ello"); string B("HA"); B.insert(1, A); // B变为"HelloA",意思是从B的第一位开始插入A字符串 B.insert(1, "yanchy ", 3); // 表示从B的索引为1的地方开始插入"yanchy"的后3位 B.insert(1,A,2,2);//从B的第一位开始插入A的第二位开始的前2位 B.append("123"); //追加"123"到B的末尾 B.append("123",1);//追加"123"的前1位到B的末尾
输入
ccstring s1,s2; cin >> s1;// 使用istream的>>重载运算符读入s1 getline(cin,s1);//使用函数getline读入 getline(cin,s2,' ');//使用getline读入,并且以空格分界,空格及之后的东西全部会被忽略
查找
string s1("123456"); s1.find('1',0);//从索引0开始寻找字符1 s1.find("12",0);//从索引0开始寻找目标串12 s1.find(s2,0);//从索引0寻找s2表示的字符串
其中find_first_of()
寻找目标串第一次出现的位置;find_last_of()
用于寻找最后一次出现的位置
迭代器
string s("123"); string s2(s.begin(),s.end());//通过迭代器构造s2 transform(s.begin(),s.end(),toupper);//toupper是把小写字母变成大写字母 for (auto iter = s.begin();iter!=s.end();iter++) cout << *iter;// 通常遍历s的方法
array
头文件<array>
类似C的数组,长度恒定,不需要自己管理内存,访问时不需要实现知道长度
std:array
array<double,100> data;// 长度为100的数组,值为定义的 array<double,100> data{}; //创建一个100,各个值均为0的数组 array<double,100> data{1.0,2.0,3.0,4.0}//前四个值被赋值
访问
data[1]; // 用索引直接访问,会有越界风险 data.at(1); //避免了越界风险 std::get<idx>(array_obj); //使用模板函数get<n>()获取第n位的数据,这个n不可以是循环变量,必须是编译时可以确定的值
比较赋值
std::array<double,4> these {1.0, 2.0, 3.0, 4.0}; std::array<double,4> those {1.0, 2.0, 3.0, 4.0}; std::array<double,4> them {1.0, 3.0, 3.0, 2.0}; these == those; // 判断所有元素是否都相等 them = those; //只要类型相同,长度相同,就可以直接赋值。 them[0] = 2.0; //可以直接通过索引修改值,也可以使用at成员函数修改,避免越界。
list
提供双向链表的构建方法,底层是一个带头节点的双向循环链表,任意位置插入和删除 时间复杂度 O(1)
<list
创建
list<string> words;//创建一个空的双向链表 list<string> words(20);//创建一个有20个结点的双向链表 list<double> values(size,value);//创建一个有size个结点,每个结点的值为value的双向链表 list<double> save_value{values};//使用另一个list来初始化 list<int> a(first,last) //声明一个列表,元素初始值来源于区间指定的序列
插入,删除
names.push_front("Ian"); // 在表首插入一个字符串 names.push_back("Kitty"); // 在表尾插入一个字符串 names.emplace_front("Ian");// 更高效的插入 names.emplace_back("Kitty");// 更高效的插入 names.insert(++begin(data),"Me"); //在第一个位置插入字符串"Me" std:list<int> numbers{2,5,2,3,6,7,8,2,9}; numbers.clear(); //清空整个双向链表 numbers.remove(value);//清除整个双向链表中所有值为value的节点 numbers.remove_if([] (int n){return n%2 == 0};) //一个一元断言,以一元断言的结果进行清除 numbers.unique(); //清除整个双向链表中重复的元素 front() //容器的头部元素 back() //容器的最后一个元素 pop_front //删除头部第一个元素 pop_back //删除尾部第一个元素 a.assign(n,val); //将a中的所有元素替换成n 个val 元素 b.assign(a.begin(),a.end())//把a开始到结束的数据赋给 b
swap(a,b) 或者 a.swap(b)都可以实现 a 和 b 的交换
reverse() 逆置
排序和合并
list<string> names {"Jane","Jim","Jules","Janet"}; name.sort() //无参排序,升序排列(如果可能) list<int> myvalues{2,4,6,8,14}; list<int> yourvalues{-2,1,7,10}; myvalues.merge(yourvalues); //合并两个链表,两个链表都必须是升序,合并后,yourvalues为空 list<string> mywords{"three","six","eight"}; list<string> yourwords{"seven","four","nine"}; mywords.splice(++begin(mywords),yourwords,++begin(yourwords)); //把yourwords的一部分粘贴到mywords的指定位置 //输出链表中的元素(迭代器) mywords.splice(++std::begin(mywords),yourwords,++std::begin(yourwords)); auto it = mywords.begin(); cout<<*it<<" "; ++it; while(it!=mywords.end()) { cout<<*it<<" "; ++it; }
stack
头文件<stack>
后进先出
empty() //堆栈为空返回真 pop() //移除栈顶元素 push() //在栈顶增加元素 size() //返回栈中元素数目 top() //返回栈顶元素
deque 双端队列
deque<int> a_deque; //size =0 的deque deque<int> deques(10);//创建一个size = 10 deque<stdstring> words{"1","2","3"};//初始化 deque<int> deque_2 {deques};//使用另一个deque初始化 //deque没有 capacity 属性,deque的capacity总是和size相等
访问和修改
words.at(idx); //返回word伴随着越界检查 words[idx]; words.resize(size); words.resize(size,init_value); //其余和vector一样
添加和删除
push_front //队头插入数据 push_back //队尾插入数据 pop_front //弹出队首数据,不返回队首数据 pop_back() // 弹出队尾数据,不返回队尾数据
queue
只能访问queue
std:queue<std::string> words; std::queue<std::string> copy_words{words};//使用拷贝函数
操作方法
front() //返回第一个元素引用,如果queue为空,返回值是未定义的 back() //返回最后一个元素的引用 push(const T&obj) //在queue尾部添加一个元素的副本,通过调用底层容器的成员函数push_back()完成的 pop()//删除弹出第一个元素 size() //返回元素个数 empty() //如果没有元素的话,返回true emplace() //在队尾调用构造函数生成对象 queue<int,list<int>> q2;
deque和queue的区别
deque双端队列操作是可以在队头队尾进行入队出队操作
queue只能修改队头(删除头部元素)
双端队列
头指针 front 尾指针 rear
起始位 front == rear
但是当rear == front 时无法判断是队满还是队空。所以选择牺牲一个空间,每个尾元素后面就是rear指向的空间
则: 队满
: front = (rear + 1) % MAX_SIZE
队空
: front = rear
DeQueue.h #define MAX_SIZE 20 #define ElemType int #define OVERFLOW -2 #define ERROR -1 class de_queue{ private:ElemType* base; int front; int rear; int size; public: void InitQueue(de_queue& Q);//初始化队列 void DetroyQueue(de_queue& Q);//销毁队列 void ClearQueue(de_queue& Q);//清空队列 bool EmptyQueue(de_queue Q);//判断队列是否为空 其实这里最好也使用引用传递,值传递会内存拷贝影响性能,具 体有待后续研究 bool FullQueue(de_queue Q);//判断队列是否已满 void EnFront(de_queue& Q,ElemType e);//头插 void EnRear(de_queue& Q, ElemType e);//尾插 void DeFront(de_queue& Q, ElemType& e);//头出 void DeRear(de_queue& Q, ElemType& e);//尾出 int LengthQueue(de_queue Q);//获取队列长度 void TraverseQueue(de_queue Q,void(*f)(ElemType e));//遍历队列 } DeQueue.cpp #include <stdlib.h> #include "DeQueue.h" void de_queue::InitQueue(de_queue& Q) { Q.base = new ElemType[MAX_SIZE]; if (!Q.base) exit(ERROR); Q.front = Q.rear = 0; Q.size = MAX_SIZE; }//初始化队列 void de_queue::DetroyQueue(de_queue& Q) { Q.rear = Q.front = 0; free(Q.base); Q.size = 0; }//销毁队列 void de_queue::ClearQueue(de_queue& Q) { Q.rear = Q.front = 0; } bool de_queue::EmptyQueue(de_queue Q) { return Q.rear == Q.front ? true : false; }//判断队列是否为空 其实这里最好也使用引用传递,值传递会内存拷贝影响性能,具体有待后续研究 bool de_queue::FullQueue(de_queue Q) { return (Q.rear + 1)%Q.size == Q.front ? true:false; }//判断队列是否已满 void de_queue::EnFront(de_queue& Q, ElemType e) { if (FullQueue(Q)) exit(OVERFLOW);//判断是否满队 Q.front = (Q.front +Q.size- 1) % Q.size; *(Q.base+Q.front) = e; }//头插 void de_queue::EnRear(de_queue& Q, ElemType e) { if (FullQueue(Q)) exit(OVERFLOW); *((Q.base + Q.rear)) = e; Q.rear = ++Q.rear % Q.size; }//尾插 void de_queue::DeFront(de_queue& Q, ElemType& e) { if (EmptyQueue(Q)) exit(ERROR); e = *(Q.base+Q.front); Q.front = (Q.front+1) % Q.size; }//头出 void de_queue::DeRear(de_queue& Q, ElemType& e) { if(EmptyQueue(Q)) exit(ERROR); Q.rear = (Q.rear +Q.size- 1)%Q.size; e = *(Q.base + Q.rear); }//尾出 int de_queue::LengthQueue(de_queue Q) { return (Q.rear +Q.size- Q.front) % Q.size; }//获取队列长度 void de_queue::TraverseQueue(de_queue Q, void(*f)(ElemType e)) { int index = Q.front; while (index!=Q.rear) { f(*(Q.base+index)); index = ++index % Q.size; } }//遍历队列 main.cpp #include <stdlib.h> #include<string.h> #include <stdio.h> #include "DeQueue.h" void printQ(ElemType e) { printf("%c ",e); } int main() { char opt[20]; de_queue queue; InitQueue(queue); ElemType e; while (true) { gets_s(opt, 20); if (strcmp(opt, "enf")==0) { printf("wait for input:"); EnFront(queue, getchar()); getchar(); } else if (strcmp(opt, "enr") == 0) { printf("wait for input:"); EnRear(queue, getchar()); getchar(); } else if (strcmp(opt, "def") == 0) { DeFront(queue, e); printf("out from front:%c\n",e); } else if (strcmp(opt, "der") == 0) { DeRear(queue, e); printf("out from rear:%c\n", e); } else if (strcmp(opt, "exit") == 0) { break; } else { printf("unknow operate\n"); continue; } TraverseQueue(queue,printQ); printf("\n"); } return 0; }
STL中一些函数的用法
unique
unique(iterator t1, t2)函数删除的是序列中所以相邻的重复元素,并不是真正的删除,而是指的重复元素的位置被不重复的元素占领
如果我们要去除数组中所有重复的元素,一般需要对数组进行排序。
bitset
c++ 的bitset头文件中,类似于数组的结构,每个元素只能是0或1,每个元素只占1bit空间
bitset<4> bitset1; //无参构造,长度为4,默认每一位为0 bitset<8> bitset2(12); //长度为8,二进制保存,前面用0补充 string s = "100101"; bitset<10> bitset3(s); //长度为10,前面用0补充 char s2[] = "10101"; bitset<13> bitset4(s2); //长度为13,前面用0补充 cout << bitset1 << endl; //0000 cout << bitset2 << endl; //00001100 cout << bitset3 << endl; //0000100101 cout << bitset4 << endl; //0000000010101
操作符
bitset<4> foo (string("1001")); bitset<4> bar (string("0011")); cout << (foo^=bar) << endl; // 1010 (foo对bar按位异或后赋值给foo) cout << (foo&=bar) << endl; // 0010 (按位与后赋值给foo) cout << (foo|=bar) << endl; // 0011 (按位或后赋值给foo) cout << (foo<<=2) << endl; // 1100 (左移2位,低位补0,有自身赋值) cout << (foo>>=1) << endl; // 0110 (右移1位,高位补0,有自身赋值) cout << (~bar) << endl; // 1100 (按位取反) cout << (bar<<1) << endl; // 0110 (左移,不赋值) cout << (bar>>1) << endl; // 0001 (右移,不赋值) cout << (foo==bar) << endl; // false (0110==0011为false) cout << (foo!=bar) << endl; // true (0110!=0011为true) cout << (foo&bar) << endl; // 0010 (按位与,不赋值) cout << (foo|bar) << endl; // 0111 (按位或,不赋值) cout << (foo^bar) << endl; // 0101 (按位异或,不赋值)
此外,可以通过 [ ] 访问元素(类似数组),注意最低位下标为0,如下:
bitset<4> foo ("1011"); cout << foo[0] << endl; //1 cout << foo[1] << endl; //1 cout << foo[2] << endl; //0 // 也可以对某一位进行赋值
可用函数
count()
:求1的位数test()
:查某个下标处的元素是0 (false)还是 1(true)any()
:检查bitset中是否有1none()
:检查bitset中是否没有1all()
::检查bitset中是否全部为1
lower_bound
和 upper_bound
1、左闭右开区间里进行二分查找
lower_bound(begin(), end(), num)
:查找第一个大于或等于num的数字,返回该数字的地址,不存在则返回end(),返回地址 - begin() = 数字在数组中的下标
upper_bound(begin(), end(), num)
:查找第一个大于num的数字,返回该数字的地址,不存在则返回end,
2、在从大到小的排序数组中,重载
lower_bound( begin,end,num,greater< type>())
:查找第一个小于或等于 num 的数字
upper_bound( begin,end,num,greater< type>())
:查找第一个小于 num 的数字
堆
c++优先队列
priority_queue<int> heap; // 大根堆 priority_queue<int, vector<int>, greater<int>> heap; // 小根堆
操作
heap.top(); // 获得堆顶元素 heap.push(1); heap.pop(); // 弹出堆顶元素 heap.empty(); // 判空 heap.size(); // d
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)