学习STL标准库——容器

STL是什么?

STL是 ——标准库,是一个高效的C++标准库,是一些 容器、算法和其他一些组件的集合。
STL是目的是为了标准化组件,这样就可以不用再构建新的组件、直接使用现成的组件
STL现在是C++的一部分,被内置在你的编译器中。
STL可以保存对象,内建对象和类对象。它们能安全的保存对象,并且定义一个我们能操作它的接口。

STL组件有什么?

STL主要分为三类:容器、算法、迭代器。
1.容器: 容器是用来管理某种特定对象的集合(可以是数组、链表或者类字典)
2.算法: 算法是用来处理对象集合的元素 (比如:sort\search\copy。通过迭代器的协助,我们只需要撰写一次算法,就可以将它应用于任一容器之上,这是因为任意容器的迭代器都提供一致的借口)
3.迭代器: 用来在一个对象集合的元素上做遍历操作。这个对象可能是容器、也可能是容器的一部分。但是每一种容器都提供了他们自己的迭代器算法,而这些迭代器该了解该种容器的内部结构。

STL 中数据和容器是分离的,数据存储在容器中,使用算法进行操作,迭代器是这两者之间的粘合剂,让算法与容器进行交互。

容器的分类

在这里插入图片描述
容器主要可分为以下这么两大类:

1.序列式容器: 可序群集
其中的每个元素都有其特定的位置——这个位置取决于 插入的时间和地点,与插入的元素的值无关。
如果你以追加的方式插入六个元素,那么将它们的排列方式将和插入顺序一致。
序列式容器包含: array、vector(向量)、list(列表)、duque(双端队列)、forward list。

  • vector在头部和中间插入删除效率较低、在尾部插入删除效率较高。
  • duque在头部尾部插入删除效率较高。
  • list:如果想要频繁的在序列中间进行插入 删除操作并且不需要在序列内部进行长距离的跳转 可以选择list

2.关联容器: 已序群集
把元素按照某种规则排好序的集合——这个位置取决于特定的排列方式和插入元素的值,和插入次序无关。
序列化容器包含:set(集合)、multiset(多重集合)、map(映射)、multimap(多重映射)。

  • 关联容器按照特定的排序准则进行对元素的自动排序。 排序准则以函数形式呈现:用来比较元素值(val)或者元素键(key)。缺省情况下,可以用 operator< 进行比较。也可以定义自己的排序函数,定义出不同的排序准则。

  • 关联容器一般由二叉树构建。在二叉树中,每个元素一般都会有父节点和左节点、右节点。左子树的所欲元素都比父结点小,右子树的所有元素都比父结点大。关联容器的存放一般都和元素的类型 和 处理重复元素的方式。

  • 关联式容器可以自动的进行排序。但是这并不意味着是排序的容器。也可以进行对元素进行手动排序。自动排序意义是为了在查询某个元素时,可以获得更高的效率。可以更放心的使用二分查找法,它为对数复杂度,并非线性复杂度。如果在1000个元素中查找某个元素,只需要比较10次,而非500次比较。

容器的特点介绍:

一:序列容器:
(1)vector: 向量容器。

  • 概述: 和数组差不多,但是比数组更优越,因为数组不可以进行动态开辟,那么程序在进行运行的时候容易造成不适内存资源浪费,就是越界。vector的出现就弥补了这一缺陷,在开头和中间插入删除元素操作比较慢,在末尾插入删除比较快。底层是一个可动态增长的一位数组,内存可自增长、内次增长2倍。

  • 特点:

    • 拥有一段连续的内存地址,并且起始地址不变,可以支持很好的随机存取操作,也就是[]操作符。但是由于内存是连续的,所以在中间进行元素的插入和删除时,需要进行内存的拷贝,并且在扩 增时,需要复制一块足够大的内存并且进行内存的拷贝,这些都将大大影响vector的效率。

    • 对于在头部和中间进行插入删除元素时,需要移动内存;如果你的元素时结构或者类的话,还需要构造函数和析构函数,所以性能不高。

    • 末尾元素进行操作时,效率最高,因为一般不需要移动内存,只有当保留内存不够时需要。

    • 随机访问每个元素、所需的时间为常量。

    • 在末尾删除/添加元素所需的时间 与 元素数目没有关系。在中间或者开头 进行删除或者添加所需的时间随元素的数目呈线性变化。

    • 可动态的增加或者删除元素、内存管理自动完成。但程序员可以用reserve()函数进行内存管理

  • 迭代器失效:
    vector的迭代器在 内存重新分配的时候就会失效(因为所指向的元素在该操作之后不再相同),当把超过capacity_size()个元素插入vector中,内存会重新分配,所有的迭代器都会失效。否则指向当前元素以后的所有元素都会失效。当删除元素时,指向该删除元素的以后的所有元素的迭代器都会失效。

  • 优缺点及使用场景:

    • 优点:因为可以进行随意的存取,即:[] 操作符 和 duque.at() ,所以查询效率高。
    • 缺点:因为在头部和中间插入删除元素时,为了保证原有的相对次序,插入或者删除点之后的元素需要向后移动,所以插入删除的效率较低。
    • 适用场景:变化较小,对象简单,频繁随机访问的场景。
//针对整型定义一个vector,插入6个元素,并打印:
#include "stdafx.h"
#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
{
    vector<int> vecTemp;

    for (int i = 0; i<6; i++)
    {
        vecTemp.push_back(i);
    }

    for (int i = 0; i<vecTemp.size(); i++)
    {
        cout << vecTemp[i] <<" ";
    }

    return 0;
}

(2)deque: 双端队列容器

  • 概述:
    底层是动态开辟的二维数组。因为可以向两端发展,所以在头部或则尾部进行插入删除元素时,比较方便迅速;但是在中间进行插入删除时,就比较麻烦,需要移动其他元素。

  • 特点:

    • 是按页或者块分配存储器的,每页都有固定数目的元素。
    • duque是 vector和list折中的方案,既有list的优点,又有vector 快速访问的优点。
    • 随机访问每个元素,所需的时间为常量。
    • 在末尾或者开头进行添加元素所需要的时间和元素数目没有关系。在中间删除或者添加元素所需要的时间随元素数目 呈线性变化。
    • 可动态的增加或者删除元素,内存管理自动完成。
  • 迭代器失效:
    除了开头结尾外,在duque的任何地方添加元素都会使得duque的迭代器失效,在duque中间删除元素会使 迭代器失效,在duque的首尾部删除元素时,只会使所指向的那一个元素的迭代器失效。

  • 优缺点及适用场景:

    • 优点:支持随机访问,[] 和 duque.at(),所以查询效率高,两端可以进行 pop(出)和push(入)。
    • 缺点:不适用中间操作;占用内存多。
    • 适用场景:既要频繁的随机存取,又要关心两端数据元素的插入删除情况。
//声明了一个浮点类型的dque,并在容器头部插入6个元素,最后打印出所有元素
#include "stdafx.h"
#include <iostream>
#include <deque>

using namespace std;

int main(int argc, char* argv[])
{
    deque<float> dequeTemp;

    for (int i = 0; i<6; i++)
    {
        dequeTemp.push_back(i);
    }

    for (int i = 0; i<dequeTemp.size(); i++)
    {
        cout << dequeTemp[i] << " ";
    }

    return 0;
}

(3)list: 列表容器

  • 概述:
    • 底层是带头结点的双向链表容器
    • 元素也可以放在堆上,每个元素都是放在一块内存上,它的内存可以是不连续的,通过指针来进行数据的访问。 这个特点使得它的随机存取变得很没有效率,因为它不能提供 [] 的重载,但是又因为链表的性质,所以又可以很方便的进行插入删除操作。
  • 特点:
    • 没有空间预留,所以每一个元素都会给分配一块内部,没删除一个元素都会释放它所占用的内存
    • 不管在链表哪里都可以很方便的进行插入删除元素,不需要移动元素,也不需要每一个元素都进行构造、析构函数,所以常被用来当做 插入删除元素容器。
    • 访问开始和最后 两个元素最快,其他元素时间一样。
    • 不能随机访问一个元素
    • 可双向遍历
    • 在开头、末尾或者中间部位 删除或者添加元素的时间都是常量,可动态的增加、删除元素、内存管理自动完成。
  • 迭代器失效:
    增加元素会使迭代器失效。删除元素时,除了指向该元素的迭代器之外,其他迭代器不会失效。
  • 优缺点及适用场景:
    • 优点:内存不连续、动态操作,插入删除效率高
    • 缺点:不能进行随机访问,相比于vector 占用内部多
    • 适用场景:经常进行插入删除操作 并且 不经常进行随机访问的场景。
//以下例子产生一个空list,准备放置字符,然后将 'a' 至 'z' 的所有字符插入其中,利用循环每次打印并移除集合的第一个元素,从而打印出所有元素:

#include "stdafx.h"
#include <iostream>
#include <list>

using namespace std;

int main(int argc, char* argv[])
{
    list<char> listTemp;

    for (char c = 'a'; c <= 'z'; ++c)
    {
        listTemp.push_back(c);
    }

    while (!listTemp.empty())
    {
        cout <<listTemp.front() << " ";
        listTemp.pop_front();
    }

    return 0;
}
  • (4)forward list: 单向链表
  • 特点:
    • 不可双向遍历、只可从头到尾的遍历 特性类似于 list

二、关联容器:

  • (1)set: 集合
  • 概述
    底层:红黑树(平衡二叉树的一种),内部元素根据其值进行自动排序。每个元素都只出现一次,不允许重复。
  • 特点:
    • set中的元素都是排好序的;
    • set集合中的元素没有重复的;
    • set和map进行插入删除操作时,相比于其他容器的效率更高,因为不需要进行内存移动和内存拷贝。
  • 优缺点和适用场景:
    • 优点:使用平衡二叉树构成,便于元素查找、且保证了元素的唯一性,并自动排序。
    • 缺点:每次插入的时候,都要调整红黑树,有效率有一定的影响。
    • 适用场景:经常查找一个元素是否在 群集中需要排序的场景。

例子:

举例演示 set的两个特点:

#include "stdafx.h"
#include <iostream>
#include <set>

using namespace std;

int main(int argc, char* argv[])
{
    set<int> setTemp;

    setTemp.insert(3);
    setTemp.insert(1);
    setTemp.insert(2);
    setTemp.insert(1);

    set<int>::iterator it;
    for (it = setTemp.begin(); it != setTemp.end(); it++)
    {
        cout << *it << " ";
    }

    return 0;
}
//输出结果:1 2 3。一共插入了4个数,但是集合中只有3个数并且是有序的,可见之前说过的set集合的两个特点,有序和不重复

当set集合中的元素为结构体时,该结构体必须实现运算符‘<’的重载:

#include "stdafx.h"
#include <iostream>
#include <set>
#include <string>

using namespace std;

struct People
{
    string name;
    int age;

    bool operator <(const People p) const
    {
        return age < p.age;
    }
};

int main(int argc, char* argv[])
{
    set<People> setTemp;

    setTemp.insert({"张三",14});
    setTemp.insert({ "李四", 16 });
    setTemp.insert({ "隔壁老王", 10 });

    set<People>::iterator it;
    for (it = setTemp.begin(); it != setTemp.end(); it++)
    {
        printf("姓名:%s 年龄:%d\n", (*it).name.c_str(), (*it).age);
    }

    return 0;
}

/*
输出结果
姓名:王二麻子 年龄:10
姓名:张三 年龄:14
姓名:李四 年龄:16 
*/ 
//可以看到结果是按照年龄由小到大的顺序排列。另外string要使用c_str()转换一下,否则打印出的是乱码。
  • (2)multiset: 多重集合

    • 性质与list类似,但是multiset允许重复元素, 可包括多个值相同的元素。
  • (3)map: 映射

    • 概述:
      • map底层是 红黑树。其元素都是 键值/实值 构成的一个对组。 每个元素都有一个键,是排序准则的基础,每个键都只能出现一次,不允许重复。
      • map主要是用于 一对一的映射关系。map内部自建一颗红黑树,这棵树具有对数据自动排序的功能,所以在map内部所有的数据元素都是有序的。比如在一个班级中,每一个学生的学号和姓名是一一对应的。
    • 特点:
      • 自动建立起 key-val的对应关系。key和val可以是任意的类型。
  • 4)multimap: 多重映射
    3.容器适配器: 提供顺序容器的特殊接口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值