目录
一、STL诞生的背景是什么?
二、STL是什么?
三、STL中的算法与迭代器
四、STL中的容器
五、容器的分类
string容器
vector容器
deque容器
stack容器
queue容器
list容器(链表)
set/multiset容器
pair使用-pair对组的创建
map/multimap容器
为了建立一种可重复利用的东西,提高代码的复用性,因此建立了数据结构和算法的一套标准,即STL。
STL(Standard Template Library),中文名标准模板库,大体分为六大组件,分别为:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器(由于本文章的重点在于STL中的容器,因此想更多知道STL的相关内容请上网搜索)。
想要学习容器,那算法与迭代器的了解必不可少。
1.算法分为两种:
内容 | 举例 | |
质变算法 | 运算过程中更改区间内的元素内容 | 拷贝,替换,删除等 |
非质变算法 | 运算过程中不改变区间内的元素内容 | 查找、计数、遍历、寻找极值等 |
2.迭代器:容器与算法之间的粘合剂
std::vector::iterator itBegin = v.begin();//起始迭代器 指向容器中第一个元素
std::vector::iterator itEnd = v.end();//结束迭代器 指向容器中最后一个元素的下一个位置
//1.遍历方式
while (itBegin != itEnd)
{
std::cout << *itBegin << std::endl;
itBegin++;
}
//2.第二种遍历
for (std::vector::iterator it = v.begin(); it != v.end(); it++)
{
std::cout << *it << char(10);
}
//3.第三种遍历 利用STL提供的遍历算法 需包含头文件#include
void myPrint(int val)
{
std::cout << val << char(10);
}
for_each(v.begin(),v.end(),myPrint);
STL中的容器就是将运用最广泛的一些数据结构实现出来,哪怕你不懂这些数据结构,也可以通过STL容器进行使用,常用的数据结构为:数组,链表,树,栈,队列,集合,映射表等。容器的使用需包含所对应的头文件,使用string需包含#include
常用容器 | 特点 |
string | c++中的字符串。字符串对象是一种特殊类型的容器,专门设计来操作的字符序列 |
vector(使用频率最高) | vector数据结构与数据非常相似,也称为单端数组,可以动态扩展 |
deque(双端队列) |
双端队列,可以对头端进行插入和删除,且内部有中控器来维护每段缓冲区中的内容 |
stack(栈) |
是一种先进后出的数据结构,只有一个出口,且不允许有遍历行为 |
queue(队列) | 是一种先进先出的数据结构,有两个出口,只有队头和队尾才可以被外界使用,不允许有遍历行为 |
list(链表) |
是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的,是一个双向循环链表 |
set/multiset |
所有元素在插入时会自动被排序,set中不允许有重复元素,miltiset中允许有重复的元素 |
map/multimap |
map中所有元素都是pair,pair中第一个元素为key(键值),第二个元素为value(实值),所有元素都会根据元素的键值自动排序 |
string s1(); 创建一个空的字符串
string s2(const char* str); 使用字符串str初始化
string s3(const string& str); 使用一个string对象(str)初始化另一个string对象
string s4(int n,char c) 使用n个字符c初始化
string& operator=(const char* str); char*类型字符串赋值给当前的字符串
string& operator=(const string str); 把字符串str赋给当前的字符串
string& operator=(char str); 字符赋值给当前的字符串
string& assign(const char* str); 把字符串str赋给当前的字符串
/*std::string str1;
str1.assign("hello world");*/
string& assign(const char* str,int n); 把字符串str的前n个字符赋给当前的字符串
string& assign(const std::string &str); 把字符串str赋给当前字符串
string& assign(int n,char str); 用n个字符str赋给当前字符串
string& operator +=(const char *str) /*std::string str1 = "Hello";
str1 += " Word";*/
string& operator +=(const char c)
string& operator +=(const string *str) //前三个都为重载+=操作符
string& append(const char* s) 把字符串s连接到当前字符串结尾
string& append(const char* s,int n) 把字符串s的前n个字符连接到当前字符串尾部
string& append(const string &s)
string& append(const char* s,int pos,int n) 字符串s中从pos开始的n个字符连接到字符串结尾
//查找
int find(const string& str,int pos=0) const; 查找str第一次出现的位置,从pos开始查找
//std::string str1 = "kjvdesghjdka";
//short pos = str1.find("de"); pos=3 查不到则返回-1
int find(const char* s,int pos=0) const; 查找s第一次出现的位置,从pos开始查找
int find(const char* s,int pos,int n) const; 从pos位置查找s的前n个字符第一次位置
int find(const char c,int pos=0) const; 查找字符c第一次出现的位置
int rfind(const string& str,int pos=npos)const; rfind是查找最后一次出现的位置
int rfind(const char* s,int pos=npos)const;
int rfind(const char* s,int pos,int n)const;
int rfind(const char c,int pos=0)const;
//替换
string& replace(int pos,int n,const string& str); 替换从pos开始n个字符为字符串str
/*std::string str1 = "abcdesghjdka";
str1.replace(0, 3, "acb");*/
string& replace(int pos,int n,const char* s); 替换从pos开始的n个字符为字符串s
int compare(const char* s) const;
/*std::string str1 = "abcdesghjdka";
std::string str = "sad";
int pos = str1.compare(str); -1*/
int compare(const string& s) const;
char& operator[](int n); 通过[]方式取字符
char& at(int n) 通过at方式获取字符
str.size()
string& insert(int pos,const char* s)
string& insert(int pos,const string& str)
string& insert(int pos,int n,char c) 在指定位置pos插入n个字符c
string& erase(int pos,int n) 删除从pos开始的n个字符
string substr(int pos=0,int n)const; 返回由pos开始的n个字符组成的字符串
string substr(int n) 返回第n个字符之后的字符串
vector v; 使用模板实现类实现,默认构造函数
vector(const vector &vec) 拷贝构造函数
vector(v.begin(),v.end()); 将v[begin(),end())区间中的元素拷贝给本身 前闭后开
/*std::vectorv;
v.push_back(1);
v.push_back(3);
v.push_back(2);
std::vectorv1(v.begin(), v.end()); */
vector(n,elem) 将n个elem拷贝给本身
vector& operator=(const vector &vec); 重载等号操作符
assign(v.begin(),v.end()) 将[begin(),end())区间中的数据拷贝赋值给本身
//v2.assign(v1.begin(), v1.end());
assign(n,elem) 将n个elem拷贝赋值给本身
empty() 返回值为bool 空为ture //判断容器是否为空
capacity() 容器的容量
/*capacity()>=size()
当容器的大小等于容量时,容器每次增加多少容量,取决于算法的实现*/
size() 返回容器中元素的个数
//若容器变短,则大小改变,容量不变
resize(int num)
/*重新指定容器的长度为num,若容器变长,则以默认值0填充新位置
如果容器变短,则末尾超出容器长度的元素被删除*/
resize(int num,elem)
/*重新指定容器的长度为num,若容器变长,则以elem值填充新位置
如果容器变短,则末尾超出容器长度的元素被删除*/
push_back(ele); 尾部插入元素ele(尾插法)
pop_back(); 删除最后一个元素(尾删法)
insert(const_iterator pos,ele) 迭代器指向位置pos插入元素ele
//v1.insert(v1.begin()+1, 100);
insert(const_iterator pos,int count,ele) 迭代器指向位置pos插入count个元素ele
erase(const_iterator pos) 删除迭代器指向的元素
erase(const_iterator start,const_iterator end) 删除迭代器从[start到end)之间的元素
clear() 删除容器中所有元素
at(int idx) 返回索引dix所指的数据
operator[] 返回索引dix所指的数据
front(); 返回容器中第一个数据元素
back() 返回容器中最后一个数据元素
//实现两个容器内元素进行互换
swap(vec); //v1.swap(v2) 将v1和v2的元素进行互换
vector(v).swap(v);
//vector(v) 匿名对象,匿名对象执行完会立刻回收
//系统会按照当前v的大小给匿名对象赋予容量和大小
//举例 用swap收缩内存
//vector的容量是动态分配的,由STL内部算法实现,因此容量会大于大小,造成一定的空间浪费
for (int i{}; i < 1000000; i++)
{
v1.push_back(i);
}
std::cout << v1.capacity() << std::endl;
std::cout << v1.size() << std::endl;
v1.resize(3);
std::cout << v1.capacity() << std::endl;
std::cout << v1.size() << std::endl;
std::vector(v1).swap(v1);
std::cout << v1.capacity() << std::endl;
std::cout << v1.size() << std::endl;
//输出 1049869 1000000 1049869 3 3 3
reserve(int len); 容器预留len个元素长度(容量),预留位置不初始化,元素不可访问
//统计内存开辟次数
std::vector v;
int* p = NULL;
int num{};
//v.reserve(1000);
for (int i{}; i < 1000; i++)
{
v.push_back(i);
if (p != &v.at(0))
{
p = &v[0];
num++;
}
}
//num = 1
sort(v.begin(),v.end()); 需包含头文件#include
void printDeque(const std::dequed)
{
for (std::deque::const_iterator it = d.begin(); it != d.end(); it++)
{
// *it = 100; 无法修改
std::cout << *it << " ";
}
}
dequedeqT; 默认构造形式
deque(const deque &deq) 拷贝构造函数
deque(begin(),end()); 构造函数将[begin(),end())区间中的元素拷贝给本身
deque(n,elem) 构造函数将n个elem拷贝给本身
assign(n,elem)
assign(begin(),end())
deque& operator=(const deque& deq)
deque.size() 返回容器中元素的个数
deque.empty() 判断是否为空
deque.resize(num,elem)
/*重新指定容器的长度为num,若容器变长,则以elem填充新位置。
如果容器变短,则末尾超出容器长度的元素被删除*/
deque.resize(num);
/*重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
如果容器变短,则末尾超出容器长度的元素被删除*/
push_back(elem) 在容器尾部添加一个数据
push_front(elem) 在容器头部添加一个数据
pop_back() 删除容器最后一个数据
pop_front() 删除容器第一个数据
指定位置插入:
insert(d1.begin(),beg,end) 在d1.begin()位置插入[beg,end)区间的数据,无返回值
insert(d1.begin(),n,elem) 在d1.begin()位置插入n个elem数据,无返回值
insert(d1.begin(),elem) 在di.begin()位置插入一个elem元素的拷贝,返回新数据的位置
clear() 清空容器所有的数据
erase(d1.begin()) 删除d1.begin()位置的数据,返回下一个数据的位置
erase(d1.end()-1) 删除最后一位数据
erase(beg,end) 删除[beg,end)区间的数据,返回下一个数据的位置
front(); 返回容器中第一个数据元素
operator[] 返回索引dix所指的数据
back() 返回容器中最后一个数据元素
at(int idx) 返回索引dix所指的数据
#include
sort(d.begin(),d.end()) 对beg和end区间内元素进行排序 从小到大
stack stk stack采用模板类实现,stack对象的默认构造形式
stack(const stack &stk) 拷贝构造函数
stack& operator=(const stack &stk) 重载等号操作符
push(elem) 向栈顶添加元素
pop() 从栈顶移除第一个元素
top() 返回栈顶元素
size() 返回栈的大小
empty() 判断堆栈是否为空
queue stk queue采用模板类实现,queue对象的默认构造形式
queue(const queue &stk) 拷贝构造函数
queue& operator = (const queue &stk) 重载等号操作符
back() 返回最后一个元素
front() 返回第一个元素
pop() 从队头移除第一个元素
push(elem) 向队尾添加元素
size() 返回队列的大小
empty() 判断队列是否为空
std::list::iterator it = l.begin();
it++;//支持双向
it--;
//it = it + 1; 不支持
(6)优点:采用动态存储分配,不会造成内存浪费和溢出;
链表执行插入和删除十分方便快捷,修改指针即可,不需要移动大量元素。
(7)缺点:容器遍历速度没有数组快,得去找地址;
占用空间大,还得保存指针域。
(8)总结: 链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大
(9)重要性质:插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的
list lst list采用模板类实现,对象的默认构造形式
list(const list& lst) 拷贝构造函数
list(beg,end) 构造函数将[beg,end)区间中的元素拷贝给本身
list(n,elem) 构造函数将n个elem拷贝给本身
list& operator=(const list &lst) 重载等号操作符
assign(n,elem) 将n个elem拷贝赋值给本身
swap(lst) 将lst与本身的元素互换
assign(beg,end) 将[beg,end)区间中的数据拷贝赋值给本身
size() 返回容器中元素个数
empty() 判断容器是否为空
resize(num)
/*重新指定容器的长度为num,若容器变长,则以默认值填充
如果容器变短,则末尾超出容器长度的元素被删除*/
resize(num,else)
/*重新指定容器的长度为num,若容器变长,则以elem填充
如果容器变短,则末尾超出容器长度的元素被删除*/
push_back(elem) 在容器尾部加入一个元素
pop_back() 删除容器中最后一个元素
push_front(elem) 在容器开头插入一个元素
pop_front(); 从容器开头移除第一个元素
insert(pos,elem) 在pos位置插入elem元素的拷贝,返回新数据的位置
insert(pos,n,elem) 在pos位置插入n个elem数据,无返回值
insert(pos,beg,end) 在pos位置插入[beg,end)区间的数据,无返回值
clean() 移除容器中的所有数据
erase(beg,end) 删除[beg,end)区间的数据,返回下一个数据的位置
erase(pos) 删除pos位置的数据,返回下一个数据的位置
remove(elem) 删除容器中所有与elem值匹配的元素
front() 返回第一个元素
back() 返回最后一个元素
reserve();
(2)排序:
sort() 从小到大
sort(myCompare) 从大到小
bool myCompare(int v1,int v2)
{
return v1>v2;
}
//回调函数 指定排序规则
/*将Person自定义数据类型进行排序,Person中的属性有姓名、年龄、身高
规则:按照年龄进行升序,如果年龄相同按照身高进行降序*/
bool comparePerson(Person& p1, Person& p2)
{
if (p1.m_Age == p2.m_Age)
{
return p1.m_Height > p2.m_Height;
}
return p1.m_Age < p2.m_Age;
}
void test01()
{
L.sort(comparePerson);
}
set& operator=(const set &st)
set(const set &st)
set st
empty()
swap(st)
size()
erase(beg,end)
erase(elem) 删除容器中值为elem的元素
erase(pos)
clear()
insert(elem)
find(key) /*查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回set.end()*/
std::set::iterator pos = s1.find(1);
if (pos != s1.end())
{
std::cout << "找到元素" << *pos << std::endl;
}
count(key)
/*统计key的元素个数
对于set而言要么是0要么是1*/
利用仿函数,可以指定排序规则 在未插入之前就指定排序规则
class MyCompare
{
public:
bool operator()(int r1, int r2)const
{
return r1 > r2;
}
};
std::sets2;
自定义的数据类型都得指定排序规则,否则无法插入
class comparePerson
{
public:
bool operator()(const Person& s1,const Person& s2)const
{
return s1.m_age > s2.m_age;
}
};
std::sets1;
成对出现的数据,利用对组可以返回两个数据。
std::pair p(value1,value2)
std::pairp("Tom", 20);
std::cout << "姓名: " << p.first << " 年龄: " << p.second << std::endl;
std::pair p = std::make_pair(value1,value2)
std::pairp2 = std::make_pair("Jerry", 30);
std::cout << "姓名: " << p2.first << " 年龄: " << p2.second << std::endl;
map(const map &mp);
map mp;
map& operator=(const map &map);
size()
empty()
swap()
insert(elem)
m.insert(std::pair(1, 10));
m.insert(std::make_pair(2, 20)); //好记
m.insert(std::map::value_type(3, 30));
m[4] = 50; //[]不建议插入,但是可以利用key访问到value
erase(beg,end)
erase(key)
erase(pos)
clear()
find(key)
//查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回map.end()
count(key)
//统计key的元素个数
图文参考:
https://www.bilibili.com/video/BV1et411b73Z?p=1&vd_source=dc3686d454b60a8bbdc03949afb2b41f(第一次写csdn,若各位大佬什么好的建议或者哪里写错了,还请不吝赐教!!)