C++从入门到精通【Day7】 STL—常用容器

一、STL的基础知识

1.STL基本概念

        ···STL(Standard Template Library)标准模板库

        ···STL的诞生是为了建立数据结构和算法的一套标准

        ···STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)

        ···容器和算法之间通过迭代器进行无缝连接

        ···STL几乎所有的代码都采用了模板类或者模板函数

2.STL六大组件

STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

···容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据

···算法:各种常用的算法,如sort、find、copy、for_each等

···迭代器:容器和算法之间沟通的桥梁

···仿函数:行为类似函数,可作为算法的某种策略

···适配器:用于修饰容器或者仿函数或者迭代器接口

···空间配置器:负责空间的配置与管理

(1)容器

STL容器就是将运用最广泛的一些数据结构实现出来

常用的数据结构:数组、链表、树、栈、队列、集合、映射表等

容器分为 序列式容器 和 关联式容器 两种:

        序列式容器:强调值的排序,序列实容器中的每个元素均有固定的位置,也就是说容器中数据的排序是按传入顺序排序的。

        关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系,也就是说容器中数据的排序是按某种规则排序的,比如升序、降序。

(2)算法

算法(algorithms) 是用有限的步骤 来解决逻辑上或数学上的问题

算法分为:质变算法 和 非质变算法

质变算法:在运算过程中会更改区间内的元素的内容,例如拷贝、替换、删除等等

非质变算法:在运算过程中不会更改区间内的元素内容,如查找、计数、遍历等等

(3)迭代器

迭代器(iterator) 提供方法使得 算法 能够访问某个容器所含的各个元素、而又无需暴露该容器的内部表示方式。每个容器都有自己专属的迭代器

迭代器的使用非常类似于指针

迭代器种类
种类    功能支持运算
输入迭代器对数据只读访问支持++、==、!=
输出迭代器对数据只写访问支持++
前向迭代器读写,并能向前操作(++)

支持++、==、!=

双向迭代器读写,并能向前和向后操作(++和 - -)支持++、- -
随机访问迭代器读写,可以以跳跃的方式访问任意数据,是功能最强大的迭代器支持++、- -、[n]、-n、<、<=、>、>=

常用的容器中迭代器种类为双向迭代器 和 随机访问迭代器,所以只学习这两个迭代器

3.STL的运用

假设我们要使用vector容器,for_each算法,它们的迭代器为vector<int>::iterator

例:

#include <vector>    //使用前要包含头文件
#include <algorithm>

int main()
{   
    vector<int> v1; //创建一个vector容器
   
   //向容器中插入数据
   v1.push_back(10);
   v1.push_back(20);
   v1.push_back(30);
    //通过迭代器访问容器中的数据
    vector<int>::iterator itBegin = v1.begin(); //起始迭代器  指向容器中第一个元素
    vector<int>::iterator itEnd = v1.end(); //结束迭代器  指向容器中最后一个元素的下一个位置
    //第一种遍历方式
    while (itBegin != itEnd)
    {
        cout << *itBegin << endl;
        itBegin++;
    }
    //第二种遍历方式
    for(vector<int>::iterator it = v1.begin(); it !=v1.end(); it++)
    {
         cout << *it << endl;
    }    
    //第三种遍历方式,利用STL提供的for_each算法
    for_each( v1.begin(), v1.end() , [ ] ( int a ){ cout << a << endl;});
    return 0;
}

容器可以存放自定义类型数据,容器中也能嵌套容器,例如vector嵌套vector,就是一个二维数组

例:

#include <vector>    //使用前要包含头文件
#include <algorithm>

int main()
{   
    vector<vector<int>>V; //创建一个大容器V

    //创建小容器
    vector<int> v1;
    vector<int> v2;
    vector<int> v3;
    //向小容器插入数据
    for(int i = 0 ; i < 4 ; i++)
    {
        v1.push_back(i+1);
        v2.push_back(i+2);
        v3.push_back(i+3);
    }
    //将小容器插入大容器中
    V.push_back(v1);
    V.push_back(v2);
    V.push_back(v3);
    //遍历大容器中的数据
    for(vector<vector<int>>::iterator it = V.begin(); it != V.end() ; it++)
    {
        for(vector <int>::iterator it2 = it->begin(); it2 != it->end(); it2++) // it此时表示小容器
        {
            cout << *it2 << " ";
        }
        cout<<endl;
    }
    return 0;
}

二、string容器

        本质:string是C++风格的字符串,而string本质上是一个类,类内部封装了char*指针,管理这个字符串,是一个char*型容器。

        特点:string类内部封装了很多成员方法,例如:find、copy、delete、replace、insert;string管理char*所分配的内存,不用担心赋值越界和取值越界,由类内部进行负责

1.string的构造

int main()
{
    string s1; //默认构造,创建空的字符串
    
    const char * ch = "hello world"; 
    string s2(ch);  //使用字符串ch初始化

    string s3(s2); //拷贝构造

    string s4(10 , 'a') //构造连续10个a的字符串
}

2.string的赋值

    string str1;
    str1 = "hello world";

    string str2;
    str2 = str1;

    string str3;
    str3 = "a";

    string str4;
    str4.assign("hello world");

    string str5;
    str5.assign("hello world",5); // 截取前5个字符

    string str6;
    str6.assign(str1);

    string str7;
    str7.assign(5,'a'); // 赋值5个a

3.string的字符串拼接

    string str1 = "hello";
    str1 += " world";

    string str2;
    str2 += str1;

    string str3;
    str3.append("hello world");

    string str4;
    str4.append("hello world", 5); //截取前五个字符
    
    string str5;
    str5.append(str1, 5 , 6); //截取从第5个字符开始,截取6个字符

4.string的查找和替换

查找:查找指定字符串是否存在             替换:在指定的位置替换字符串

    string str1="Hello World";

    //查找
    str1.find("World"); //查找并返回World的位置下标,找不到则返回-1

    str1.rfind("World");//rfind是从右往左查找并返回World的位置下标
    
    //替换
    str1.replace(0,5,"Hi");//第0到第五个字符替换成Hi

5.string的字符串比较

比较方式:

        字符串比较是按字符的ASCⅡ码进行对比,从左到右一个个比较字符

        ···相等=返回0

        ···大于>返回1

        ···小于<返回-1

    string str1="Hello";
    string str2="World";

    str1.compare(str2);//返回两个字符串比较的结果

6.string的访问和修改

    string str1="Hello";
    
    //通过 [ ]访问单个字符串
    for(int i = 0; i < str1.size(); i++)
    {
        cout << str1[i];
    }

    //通过at访问单个字符串
    for(int i = 0; i < str1.size(); i++)
    {
        cout << str1.at(i);
    }

    //通过substr访问字符串的子串
    str1.substr(0,2); //访问第一到第三这个区间的子串

    //通过[ ]修改单个字符串
    str1[0] = 'h'; //修改第一个字符

    //通过at修改单个字符串
    str1.at(3) = 'L'; //修改第四个字符

7.string的插入和删除 

    string str1="Hello";
    
   str1.insert(4," World"); //从第五个元素开始插入" World"
   str1.erase(4,10); //删除从第五个元素开始到第十个元素之间的字符

三、vector容器

        vector容器和数组非常相似,也成为单端数组,不同在于vector可以动态扩展,而数组是静态空间大小规定后不能改变。

        动态扩展并不是在原空间后接新空间,而是寻找更大的空间,然后将原数据拷贝到新空间,释放原空间。

        vector容器的迭代器是支持随机访问的迭代器

1.vector的构造

    vector<int> v1; //默认构造 无参构造

    vector<int> v2(v1.begin(),v1.end()); //通过区间方式进行构造

    vector<int> v3(10,50); //通过n个elem方式构造,赋予10个50

    vector<int> v4(v3); //拷贝构造

2.vector的赋值

vector<int>v1;
for (int i = 0; i< 10 ; i++)
{
    v1.push_back(i); //依次插入元素i
}

vector<int>v2;
v2 = v1; //把v1的数据都赋予v2

vector<int>v3;
v3.assign(v1.begin(),v1.end());

vector<int>v4;
v4.assign(10 , 50) //赋予10 个 50

3.vector的容量和大小

函数原型:

empty(); //判断容器是否为空

capacity(); //容器的容量

size(); //返回容器中元素的个数

resize(int num); //重新指定容器的大小为num

                         //若容器变长,则默认值填充新位置,若容器变短,则超出容器长度的元素被删除

resize(int num,elem); //重新指定容器的大小为num

                        //若容器变长,则以elem填充新位置,若容器变短,则超出容器长度的元素被删除

4.vector的插入和删除

函数原型:

push_back(ele); //在尾部插入元素ele

pop_back();  //删除最后一个元素

insert(const_iterator pos , ele); //迭代器指向位置pos插入元素ele

insert(const_iterator pos , int coout,ele); //迭代器指向位置pos插入count个元素ele

erase(const_iterator pos); //删除迭代器指向位置pos的元素

erase(const_iterator start , const_iterator end); //删除迭代器从start到end之间的元素

clear(); //删除容器中所有元素

5.vector数据存取

函数原型:

通过at(idx); //返回索引idx所指的数据

通过运算符[idx]; //返回索引idx所指的数据

front();  //返回容器中第一个数据元素

back();  //返回容器中最后一个数据元素

6.vector互换容器

互换容器就是实现两个容器内元素进行互换

函数原型:

v1.swap(v2);将v2与本身的元素互换

7.vector预留空间

预留空间的意义在于减少vector在动态扩展容量时的扩展次数

函数原型:

reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问


四、deque容器

        deque容器是一种双端数组,可以对头端进行插入删除操作。

deque和vector的区别:

···vector对于头部数据的插入删除效率低,数据量越大,效率越低

···deque相对而言,对头部的插入删除速度比vector快

···vector访问元素的速度比deque快

1.deque的构造

#include "iostream"
using namespace std;
#include "deque"

void printDeque( deque<int >&d)
{
    for(deque<int>::iterator it = d.begin() ; it != d.end() ; it++)
    {
        cout<<  *it <<" ";
    }
}
int main()
{   
    deque<int> d1;  //构造d1;
    for(int i = 0 ; i < 10 ; i++)
    {
        d1.push_back( i ) ;
    }
    deque<int>d2(d1.begin() , d1.end() ); // 构造d2为d1的首元素到尾元素
    deque<int>d3(10 , 5); //构造d3,10个5
    deque<int>d4(d3); // 拷贝构造

    printDeque(d1); cout<<endl;
    printDeque(d2); cout<<endl;
    printDeque(d3); cout<<endl;
    printDeque(d4);
    return 0;
}

2.deque的赋值

    deque<int> d1;  //构造d1;
    for(int i = 0 ; i < 10 ; i++)
    {
        d1.push_back( i ) ; //依次插入元素
    }
    deque<int> d2;
    d2 = d1;  //使用赋值符号赋值
    deque<int> d3;
    d3.assign(d1.begin() , d1.end() );  //使用成员函数assign
    deque<int> d4;
    d4.assign(10 , 5); //使用assign赋予10个5

3.deque的大小操作

函数原型:

deque.empty();  //判断容器是否为空

deque.size();     //返回容器中元素的个数

deque.resize(num);        //重新指定容器的长度为num,若容器变长,则以默认值填充新位置

                                        //若容器变短,则删除末尾超出长度的元素

deque.resize(num , elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置

                                        //若容器变短,则删除末尾超出长度的元素

例:

    deque<int> d1;  //构造d1;
    for(int i = 0 ; i < 10 ; i++)
    {
        d1.push_back( i ) ; //依次插入元素
    }
    if(d1.empty())
    {
        cout<<"d1为空"   <<endl;
    }
    cout<<"d1的大小为"<<d1.size();

4.deque的插入和删除

函数原型:

两端插入操作:

···push.back(elem); //在容器尾部添加一个数据

···push.front(elem); //在容器头部插入一个数据

···pop.back();        //删除容器最后一个数据

···pop.front();         //删除容器第一个数据

指定位置操作:

···insert( pos , elem) ; //在pos位置插入一个elem元素,并返回新数据的位置

···insert( pos , n . elem); //在pos位置插入n个elem数据,无返回值

···inset( pos , beg ,end); //在pos位置插入[beg , end]区间的数据,无返回值

···clear(); //清空容器所有数据

```erase(beg , end) ; //删除[beg , end]区间的数据,返回下一个数据的位置

···erase(pos) ; //删除pos位置的数据,返回下一个数据的位置

5.deque的数据存取

函数原型:

···at(int idx); //通过at函数返回索引idx所指的数据

···[ idx ] ; //通过[]运算符返回索引idx所指的元素

···front() ; //返回容器中第一个数据元素

···back() ; //返回容器中最后一个数据元素

五、stack容器

1.stack(栈)基本概念

        stack(栈)是一种先进后出的数据结构,它只有一个出口,栈中只有栈顶的元素可以被外界使用,因此栈不允许有遍历操作。

        栈可以判断容器是否为空,可以返回元素个数

2.stack常用接口

构造函数:

···stack<T> stk;  //默认构造

···stack(const stack &stk) ; //拷贝构造

赋值操作:

···stack& operator=(const stack &stk);  //重载等号操作符

数据存取:

···push(elem) ; //向栈顶添加元素

···pop() ; //从栈顶移除第一个元素

···top() ; //返回栈顶元素

大小操作:

···empty() ; //判断堆栈是否为空

···size() ; //返回栈的大小

六、queue容器

1.queue(队列)基本概念

        queue(队列)是一种先进先出的数据结构,他有两个出口。

        队列从一端新增元素,从另一端移除元素。

        队列中只有队头和队尾才可以被外界使用,因此队列中不允许有遍历行为。

        队列中插入数据称为入队,移出数据称为出队。

2.queue常用接口

构造函数:

···queue<T> que ;  //默认构造

···queue(const queue &que); //拷贝构造

赋值操作:

···queue& operator=(const queue &que); //重载等号操作符

数据存取:

···push(elem) ; //向队尾添加元素

···pop() ;  //从队头移除一个元素

···back(); //返回最后一个元素

···front(); //返回第一个元素

大小操作:

···empty() ; //判断队列是否为空

···size() ; //返回队列的大小

七、list容器

1.list(链表)基本概念

        list(链表)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。

        链表是由一系列结点组成的。

        链表的结点由存储数据元素的数据域  和  存储下一结点地址的指针域  组成。

        STL中的链表是一个双向循环链表。

优点:

        可以对任意位置进行快速插入和删除

        采用动态存储分配,不会造成内存浪费和溢出

缺点:

        容器遍历速度没有数组快,占用空间比数组大

2.list容器的常用接口

构造函数:

···list<T> lst1;  //默认构造

···list(beg , end) ;  //将[beg , end]区间中的元素拷贝给本身

···list(n , elem); //将n个elem元素拷贝给本身

···list(const list &lst); //拷贝构造函数

赋值与交换:

···assign(beg , end) ; // 将[beg , ned]区间中的数据拷贝赋值给本身

···assign(n , elem) ; //将n个elem拷贝赋值给本身

···list& operator=(const list &lst); //重载等号操作符

···swap(lst) ; //将lst与本身元素互换

大小操作:

···size();  //返回容器中元素的个数

···empty() ; //判断容器是否为空

···resize(num);   //重新指定容器的长度为num,若容器变长,则以默认值填充新位置

                          //若容器变短,则删除末尾超出长度的元素

···resize(num , elem);   //重新指定容器的长度为num,若容器变长,则以elem填充新位置

                          //若容器变短,则删除末尾超出长度的元素

插入操作:

···push_back(elem); //在容器尾部添加一个元素

···push_front(elem); //在容器头部插入一个元素

···insert(pos , elem); //在pos位置插入elem元素,返回新数据的位置

···insert(pos , n ,elem); //在pos位置插入n个elem元素,无返回值

···insert(pos, beg , end); //在pos位置插入[beg ,end]区间的数据,无返回值

删除操作:

···pop_back() ;// 删除容器中最后一个元素

···pop_front(); //从容器开头移除第一个元素

···clear(); //删除容器所有数据

```erase(beg , end) ; //删除[beg , end]区间的数据,返回下一个数据的位置

···erase(pos) ; //删除pos位置的数据,返回下一个数据的位置

···remove(elem); //删除容器中所有与elem值匹配的元素

存取操作:

···front(); //返回第一个元素

···back(); //返回最后一个元素

反转和排序操作:

···reverse() ; //反转链表

···sort() ; //链表排序

八、set / multiset容器

        set / multiset容器属于关联式容器,所有元素都会在插入时自动被排序,底层结构是用二叉树实现的。

1.set的构造和赋值

构造:

···set<T>  st;  //默认构造

···set(const set &st);  //拷贝构造

赋值:

···set& operator = (const set &st); //重载等号操作符

2.set的大小和交换

···size();   //返回容器中元素的数目

···empty();  //判断容器是否为空

···swap(st);  //交换两个集合容器

3.set的插入和删除

···insert(elem);  //在容器中插入元素

···clear();  //清除容器所有元素

···erase(pos);  //删除pos位置的元素,返回下一个元素的位置

···erase(beg , end); //删除[beg , end]区间上的元素,返回下一个元素的位置

···erase(elem);  //删除容器中值为elem的元素

4.set的查找和统计

···find(key) ;  //查找key是否存在,若存在,返回该元素的位置,若不存在,返回set.end();

```count(key);  //统计key的元素个数

5.set和multise的区别

···set容器不允许容器中有重复的元素

···multiset允许容器中有重复的元素

···set插入数据的同时会返回插入结果,表示插入是否成功

···multiset不会检测数据,因此可以插入重复数据

6.pair对组创建

对组是成对出现的两个数据,利用对组可以返回两个数据

两种创建方式:

···pair<T , T> p (value1 , value2) ;

```pair<T , T> p = make_pair (value1 , value2) ;

例:

    pair<string,int> p1("QHL",20);
    cout << p1.first << "的年龄是:" << p1.second << endl;
    pair<string,int> p2("HX",20);
    cout<<p2.first<<"的年龄是:"<<p2.second<<endl;

九、map/multimap容器

        map/multimap属于关联式容器,他们的所有元素都是pair,底层结构式用二叉树实现的。

···pair中的第一个元素为key(键值),起到索引作用,第二个元素为value(实值)

···所有元素都会根据元素的键值自动排序,可以根据key值快速查找到value

···map不允许有重复key值的元素,而multimap可以

1.map的构造和赋值

···map<T , T> mp; //默认构造

···map(const map &mp); //拷贝构造

2.map的大小和交换

···size();  //返回容器中元素的个数

···empty();  //判断容器是否为空

···swap(st);  //交换两个集合容器

3.map的插入和删除

···insert(elem); //在容器中插入元素

```clear(); //清除容器中所有的元素

```erase(pos) ; //删除pos位置上的元素,返回下一个元素的位置

```erase(beg , end) ; //删除[beg , end]区间的元素,返回下一个元素的位置

```erase(key) ;  //删除容器中值为key的元素

4.map的查找和统计

find(key); //查找key是否存在,若存在,返回该key元素的位置,若不存在,返回set.end()

count(key);  //统计key的元素个数

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值