标准模板库(Standard Template Library,STL)是惠普实验室开发的一系列软件的统称。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。虽说它主要出现到C++中,但在被引入C++之前该技术就已经存在了很长时间。STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
STL从广义上分为
- 容器
- 序列式容器(元素位置是由进入容器时机和地点来决定)
2.关联式容器(容器已经有规则,位置不是由时机和地点决定)
2.算法:
通过有限的步骤,解决问题
3.迭代器(是算法和容器的桥梁)
就是一个指针,对指针的操作基本都可以对迭代器操作
实际上,迭代器是一个类对象(++、--是因为这个类重载了这些操作符)
STL是C++的一部分,不需要额外安装什么,它被内建在编译器之内,其一个重要特点是数据结构和算法的分离。程序员可以不考虑STL具体的实现过程,只要能够熟练使用STL就可以了。
数组是容器,但不是STL的标准容器
#include<vector> 动态数组 可变数组
#include<algorithm> 算法 包含对容器进行操作的函数
基本语法:
vector<int> v 定义一个容器,指定存放类型int
v.push_back(10);
容器中可能存放基础的数据类型,也可能存放自定义数据类型
迭代器:
vector<int> :: iterator pBegin = v.begun();
vector<int> :: iterator pEnd = v.end();
当容器中的变量是类对象的时候:
vector<Person> v;
Person p1(10,20),p2(30,40),p3(50,60)
v.push_back(p1);
v.push_back(p2);
v.push_back(p3); //那么有遍历算法如下:
for (vector<Person>::iterator it = v.begin() ; it != v.end() ; it++)
{
cout<<(*it).age<< (尖括号里面是什么,取出来就是什么)
}
v.end() 是最后一个元素的下一个位置 v是一个容器变量
第一部分:Vector容器
原理图:
vector实现动态增长:
当插入新元素的时候,如果空间不足,呢么vector会重新申请更大的一块内存空间,将原空间的数据拷贝到新空间,释放旧空间的数据,再把新元素插入新申请空间
vector构造函数:
vector<T> v; //采用模板实现类实现,默认构造函数
vector(v.begin(),v.end));//将v[begin(),end())区间中的元素拷贝给本身。
vector(n,elem);//构造函数将n个elem拷贝给本身。
vector(const vector &vec);//拷贝构造函数。
//例子使用第二个构造函数我们可以...
int arr[] = {2,3,4,1,9} ;
vecpr<int> v1(arr,arr + sizeof(arr) / sizeof(int)) ;
vector常用赋值操作:
assign(beg,end);//将[beg,end)区间中的数据拷贝赋值给本身。
assign(n,elem);//将n个elem拷贝赋值给本身。
vector& operdtor=(const vector &vec);//重载等号操作符
swap(vec) ;// 将vec 与本身的元素互换。
//第一个赋值函数,可以这么写:
int arr[] = { 0,1,2,3,4};
assign(arr,arr + 5);//使用数组初始化vector
其中swap是成员函数,原理是交换指针的方向,指向对方的数组
vector大小操作:
size()://返回容器中元素的个数
empty();//判断容器是否为空
resize(int num) ;//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则妈尾超出容器长度的元索被删除。
resize(int num,elem);//重新指定容器的长度为num,若容器变长,则以 elem值填充新位置。如果容器变短、则末尾超出容器长>度的元素被删除。
capacity()://容器的容量
reserve(int len)://容器预留len个元素长度,预留位置不初始化,元素不可访问。
1.若resize之和变小了,系统会扔掉多出来的元素
2.v.capacity() 为容量(大于等于元素的个数)---容量大于等于size
vector数据存取操作:
at(int idx);//返回索引iax所指的数据,如封 idx越界,抛出out_of_range异常。
operator[]://露回索引idx 所指的数据,越界时,运行直接报错
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
vector插入和删除:
insert(const_iterator pos,int count, ele);//迭代器指向位置pos插入count个元素cle.push_back(ele) : //尾部插入元系ele
pop_back() ;//副除最后一个元素
erase(const_iterator start,const_iterator end);//删除迭代器从start 到end 之间的元素erase(const_iterator pos);//删除迭代器指向的元素
clear();//删除容器中所有元素
v.reserve()可以预留空间 (如果知道容器大概要存储元素个数,就用reserve)
reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前不能引用容器内的元素
第二部分:deque容器
原理图:
deque构造函数
include<deque>
deque<T> deqT; //默认构造形式
deque(beg,end);//构造函数将[beg,end)区间中的元素拷贝给本身。
deque(n,elem);//构造函数将n个elem拷贝给本身。
deque(const deque &deq);//拷贝构造函数。
deque的赋值操作和大小操作
assign(beg,end);/将[beg,end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
deque& operator=(const deque &deq);//重载等号操作符
swap(deq);// 将deq与本身的元素互换
deque.size(;//返回容器中元素的个数
deque.empty();//判断容器是否为空
deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num,elem);//重新指定容器的长度为num,若容器变长,则以 elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。
deque容器的插入和删除
push_back(elem);//在容器尾部添加一个数据
push_front(elem);//在容器头部插入一个数据
pop_back();//删除容器最后一个数据
pop_front();//删除容器第一个数据
第三部分:stack栈容器
有栈顶和栈底 入栈--------压栈push 、 出栈--------out栈pop
栈不能遍历,不支持随机存取。只能通过top从栈顶获取和删除元素(不提供迭代器)
“要想访问栈底元素b,要把栈顶元素a移出去”
#include<stack>
void test() {
stack<int> s1; 初始化
stack<int> s2(s1);
s1.push(10); //压栈
s1.push(20);
s1.push(30);
s1.push(100);
s1.pop(); 删除栈顶元素
s1.empty(); ///判断堆栈是否为空
s2.size(); ///返回堆栈的大小
//打印栈容器的数据
while( ! s1.empty()){
cout<<s1.top()<<” ”;
s1.pop();
}
}
第四部分:queue队列容器
入队---------队尾 出队---------------队头
注意::: 队尾:back(在左)///队头 front (在右)
不能进行遍历 --- 不提供迭代器 ------不支持随机访问
#include<queue>
queue构造函数:
queue<T> queT;//queue采用模板类实现,queue对象的默认构造形式;
queue(const queue &que);//拷贝构造函数
queue存取、插入、删除
push(elem);//往队尾添加元素
pop();//从队头移除第一个元素
back() ;//返回最后一个元素
front();//返回第一个元素
queue赋值操作
queue& operator=(const queue &que);//重载等号操作符
queue大小操作
empty() ;//判断队列是否为空
size();//返回队列的大小
第五部分:list链表
链表是由一系列的结点组成,结点包含两个域,一个数据域,一个指针域。
链表内存是非连续的,删除元素和添加元素的时间复杂度都是常数项
不需要移动数据,比数组效率高。链表只有在需要的时候才分配内存。
链表:只要你拿到第一个节点,相当于拿到了整个链表
list构造函数
list<T> 1stT;//list采用采用模板类实现,对象的默认构造形式:
list(beg, end) ;//构造函数将[beg,end)区间中的元素拷贝给本身。
list(n,elem);//构造函数将n个elem拷贝给本身。
list(const list &lst)://拷贝构造函数。
list数据元素插入和删除操作
push_back (elem);//在容器尾部加入一个元素
pop_backO;//删除容器中最后一个元素
push_front(elem)://在容器开头插入一个元素
pop_font(;//从容器开头移除第一个元素
insert(pos,elem) ://在.pos位置插elem元素的拷贝,返回新数据的位置。
insert(pos,n, elem)://在pos位置插入n个elem 数据,无返回值。
insert(pos, beg.end): //在 pos位置插入[beg, end)区间的数据,无返回值。
clear() ;//移除容器的所有数据
erase(beg, end)://删除[beg.end)区间的数据,返回下一个数据的位置。
erase(pos);//制除 pos位置的数据,返回下一个数据的位置。
remove(elem);//删除容器中所有与elem值匹配的元素。
list的一些操作:
#include<list>
void test(){
list<int> mlist1;
list<int> mlist2(10,10); /有参构造
list<int> mlist3(mlist2); //拷贝构造
list<int> mlist4(mlist2.begin(),mlist2.end());
for(list<int>:: iterator it = mlist4.begin(); it != mlist4.end() ; it ++)
{
cout<<*it<<” ”;
}
}
/list容器插入删除
void test0.(){
list<int>mlist;
mlist.push_back(100); //在尾部插入
mlist.push_front(200); ///在头部插入
mlist.insert(mlist.begin(),300);
mlist.insert(mlist.end(),400);
list<int>::iterator it = mlist.begin() ;
it++;
it++;
mlist.insert( it ,500) ;
mlist.remove(200); 删除匹配所有值,删除所有200
mlist.size() ///返回容器中元素个数
empty是否为空 resize(num) 重新指定容器的长度为num
front 第一个元素 back返回最后一个元素
//排序
void test05(){
list<int> mlist;
mlist.push_back(2);
mlist.push_back(4);
mlist.push_back(9);
mlist.push_back(7);
for(list<int>:: iterator it = mlist.begin(); it != mlist.end() ; it ++)
{ ///先直接输出
cout<<*it<<” ”;
}
mlist.sort(); 里面若加上一个bool类型的参数,就可以改变排序方式
for(list<int>:: iterator it = mlist.begin(); it != mlist.end() ; it ++)
{ ///排序之后输出
cout<<*it<<” ”;
}
}
算法sort支持可随机访问的容器,而mlist不支持随机存取
第六部分:set容器
二叉搜索树:
二叉搜索树 是指二叉树中的结点按照一定的规则进行排序,使得对二叉树中元素访问更加高效。(任意节点的元素值一定大于左子树中每一个结点的元素值,小于右子树结点的元素值)
深度----------------------层数
从普通的 二叉搜索树,到平衡二叉树------然后再到红黑树
基于二叉搜索树的树-----------------非常好查找--------插入元素自动排序
set/multiset的特性是所有元素会根据元素的值自动进行排序。set是以RB-tree为底层机制,其查找效率非常好。
set容器中不允许重复元素 ---------- multiset允许重复元素(唯一区别)
set容器只提供insert方法
set//multiset 不可通过迭代器修改元素的值,原因不用我多说吧
要真的是想要修改元素的值,就删了它再把改后的值加进来
set函数常用API:
#include<set>
//set容器初始化
void test(){
s1.insert(7); //自动进行排序,默认从小到大
s1.insert(8;
s1.insert(9);
s1.insert(10);
for(set<int>:: iterator it = s1.begin(); it != s1.end() ; it ++)
{
cout<<*it<<” ”; /你只管输入,输出就是排好序的状态
}
}
注意:之前的这么多容器应该都有remove()吧,功能是移除所有括号内的元素。
但是set里面每种元素最多只有一个,所以没有remove函数,补充remove功能的
函数是erase( )
set查找操作:
find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end ();
lower_bound(keIElem);//返回第一个key>=keyElem元素的迭代器。
upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器。
equal_range(keyElem);//返回容器中 key 与 keyElem相等的上下限的两个迭代器。
lower_bound :找第一个大于等于括号里的值的元素的迭代器
upper_bound :找第一个大于括号里的值的元素的迭代器
第七部分:对组pair
将一对值组合成一个值,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个共有函数first和second访问。
template<class T1,class T2> struct pair
void test(){
pair<int,int> pair(10,20) ///构造方法
cout<<pair.first<<” ”<<pair1.second<<endl;
pair<int , string>pair2 = make_pair( 10, “aaa”);
cout<<pair2.first<<” ”<<pair2.second<<endl;
pair<string, int> pair2 = make_pair("name",30) ;
cout << pair2.first << endl;
cout << pair2.second << endl ;
//pair=赋值
pair<string,int> pairk pair2;
cout << pair3.first << endl;
cout << pair3. second << endl ;
第八部分:map容器
map常用API
map构造函数:
map<T1,T2> mapTT; //ma默认构造函数
map(const map &mp);//拷贝构造函数
map赋值操作
map& operator=(const map &mp);//重载等号操作符
swap (mp)://交换两个集合容器
map大小操作
size0 ;//返回容器中元素的数目
empty()://判断容器是否为空
关于map的一些操作:
#include<map>
void test(){
map<int,int> mymap; //map容器模板参数,第一个参数key,二value
mymap.insert(pair<int,int>(10,10)); /第一种
mymap.insert(make_pair(20,20)); /第二种
mymap.insert(map<int,int>::value_type(30,30)); /第三种
mymap[40] = 40;
for(map<int,int>::iterator it = mymap.begin(); it !=mymap.end(); it++)
{
cout<<*(it).first<<*(it).second<<endl;
}
}
mymap[40] = 40; /发现如果key不存在,创建pair插入到map容器中,如/果发现key不存在,那么会修改key对应的value
如果通过【】方式去访问map种一个不存在key,那么map会将这个访问的key插入到map中,并赋予默认值。
map相对于set区别,map具有键值和实值,所有元素根据键值自动排序。pair的第一元素被称为键值,第二元素被称为实值。map也是以红黑树为底层实现机制。