c++—STL(六大组件)

news2024/10/5 13:04:50

一、STL概述

 

        1.  STL概述

        (1)STL定义:STL(standard template library),标准模板库,是一个高效的c++程序库,重在提高了代码的复用性;主要包含了常用的数据结构和基本算法,为广大c++程序员提供了一个可扩展的应用框架;

        体现了泛型化(模板化)程序设计的思想,在实现方面是以一种类型程序参数化的方式实现的;采用模板类实现,所以定义时需要执行存储元素的类型(基本类型或自定义类型);

      2. STL的六大组件

        (1)容器(container):各种基本数据结构

        (2)迭代器(iterator):常使用在遍历容器,又可以用在连接容器和算法;

        (3)算法(algorithm):提供了各种基本算法如sort、search等;

        (4)适配器(adapter):可改变容器(containers)、迭代器(iterators)或函数对象(function object)接口的一种组件;

        (5)函数对象(function object):用类型包装函数,将函数类型化;

        (6)分配器(allocator):内部调用malloc分配空间;

 二、容器

        1. 分类:可分为三类:

        (1)顺序容器(sequence containers),主要包括array、vector、deque、list、forward_list;

        (2)关联容器(associative containers),内部机理是树、哈希表,元素是键/值对,特别适合做查找,因为其时间复杂度较低;且大都具有自动排序功能;主要包括set、multiset、map、multimap;

        (3)无序(散列)容器(UNordered containers),内部机理是哈希表,c++11里推出:元素的位置不重要,重要的是这个元素是否在这个容器里面,主要适用于查找;主要包括:underde_set、unordered_multiset、unordered_map、unordered_multimap;

        2. vector容器  头文件#include<vector>

        (1)本质与特点

        ①vector是内置有动态数组,可以随机存取元素,可用[ ] 和 at()方法;

        ②vector尾部添加或移除元素非常快速;

        ③因为vector在中间插入会导致后面的所有元素都要重新析构与拷贝构造,效率很低;

        ④vector采用模板类实现,所以定义时需要执行存储元素的类型(基本类型或自定义类型)

        (2)主要操作

vector对象的默认构造vector<int>v1;
vector对象的带参数构造vector<int>v2(v1.begin()+1, v1.begin()+4);
vector的插入push_back、insert
vector数据存取v1.at(), v1[ ], v1.front(),
vector的删除pop_back(), clear(), erase(),
vector的迭代器begin(), end(), rbegin(), rend()
vector的大小size(), empty(), capacity(), resize(), shrink_to_fit
vector的赋值assign(), operator=, swap

vector示例:

#include <vector>
#include <iostream>
using namespace std;

class Test
{
public:
    Test()
    {
        // cout << "Test" << endl;        
    }

    Test(int t) : m_t(t)
    {
        // cout << "Test int" << endl;
    }

    Test(const Test &other)
    {
        m_t = other.m_t;
        // cout << "Test copy" << endl;
    }

    ~Test()
    {
        // cout << "~Test" << endl;
    }

    friend ostream& operator<<(ostream&out, Test &t);
    friend bool operator!=(const Test&t, int num);
    friend int operator%(const Test&t, int num);

    int m_t;
};
ostream& operator<<(ostream&out, Test &t)
{
    out<<t.m_t;
    return out;
}
bool operator!=(const Test&t, int num)
{
    return (t.m_t != num);
}
int operator%(const Test&t, int num)
{
    return (t.m_t % num);
}

template <typename T>
void print(vector<T> &vt)
{
    for(int i = 0; i < vt.size(); i++)
    {
        cout<<vt.at(i)<<" ";
    }
    cout<<endl;
}

template <typename T>
void delete_node(vector<T> &vt)
{
    for(auto it = vt.begin(); it != vt.end(); )
    {
        
        if(*it % 2 != 0)
        {
            // it = vt.erase(it);
            vt.erase(it);
        }
        else
        {
            ++it;
        }
    }
}

int main(int argc, char **argv)
{
    vector<Test> vt;
    vt.reserve(20);  //提前预留20个元素值,避免了空间不够时,频繁的拷贝构造
    for(int i = 0; i < 9; i++)
    {
        vt.push_back(Test(i+1));  //连续构造1-10
    }

    print(vt);
    delete_node(vt);  //删除其中是基数的元素值(test的m_t是奇数的)
    print(vt);

    cout<<"vt.capacity() = "<<vt.capacity()<<endl;  //输出vt容器的可容纳元素的大小,20
    vt.shrink_to_fit();  //回收未被利用的空间
    cout<<"vt.capacity() = "<<vt.capacity()<<endl;  //输出vt容器的可容纳元素的大小,4

    vt.assign(7,7);  //将7个7,赋给vt容器,注意是系统默认将vt原有的内容先清空
    print(vt);

    return 0;
}

        3. list 容器  头文件 #include<list>

        (1)本质及特点

        ①list是一个双向链表容器,适合高效地在头尾插入或者删除元素;

        ②list不支持随机存取元素,所以不支持at()方式与[ ]操作符;请注意迭代器方面it++可以,但是it+2这样就不支持了;只可遍历,不可跳跃;

        (2)主要操作

list对象的默认构造list<int> l1;也可以是自定义类型;
list对象的带参数构造list<int> l2 (l1.begin()+1, l1.end());
list的插入

l2.push_back();     l2.pop_back();

l2.push_front();     l2.pop_front();

l2.emplace_back(); l2.emplace_front();

list元素删除

l2.clear(); 

l2.erase(beg,end);

l2.erase(pos);

l2.remove(elem);

l2.remove_if();

list.unique();

list的迭代器

l2.begin();

l2.end();

l2.rbegin();

l2.rend();

list的元素存取

l2.front();

l2.back();

list的赋值

l2.assign();

l2.swap(l1);

list的大小

l2.size();

l2.empty();

l2.resiae();

l2.resize();

list的排列

l2.sort();

l2.reverse();

list对容器的操作

splice();

list示例:

#include <list>
#include <iostream>
#include <vector>

using namespace std;

template <typename T>
class Deleteor
{
public:
    bool operator()(const T &t)
    {
        return (t % 2) == 0;
    }
};

template <typename T>
void print(list<T> & other)
{
    for(auto it = other.begin(); it != other.end(); ++it)
    {
        cout<<*it<<" ";
    }
        cout<<endl;
}

int main(int argc,char **argv)
{
    list<int> l1;
    l1.emplace_back(1);
    l1.emplace_back(2);
    l1.emplace_back(3);
    l1.emplace_back(4);
    l1.emplace_back(5);
    l1.emplace_back(6);
    l1.emplace_back(7);
    l1.emplace_front(8);

    print(l1);  

    l1.insert(std::next(l1.begin(),3),9);  //std::next(l1.begin(),3)是制定插入的位置
    print(l1); 

    l1.remove(4);  //删除值等于4的元素
    print(l1);

    l1.remove_if(Deleteor<int>());  //Deleteor<int>()是删除的规则,返回的是bool(true,false)
    print(l1);                      //这里是一个函数对象,重载了函数运算符()

    l1.sort();  //对list排序,默认是从小到大
    print(l1);

    l1.sort(greater<int>());  //内建函数(对象),实现从大到小排序
    print(l1);

    l1.reverse();  //对list逆序
    print(l1);

    list<int>l2 = {1,3,5,7,9};
    list<int>l3 = {2,4,6,8,10};
    l2.merge(l3);  //将l3合并到l2上,并按照默认的从小到大方式排好序
    cout<<"l2 : ";
    print(l2);
    cout<<"l3 : ";
    print(l3);  //l3会被清空

    list<int>l4 = {7,7,7,7};

    auto it = l2.begin();
    std::advance(it,2);  //将it迭代器后退2个位置,-2就是前进2个位置
    l2.splice(it,l4);
    cout<<"l2 : ";
    print(l2);

    return 0;
}

        4. forward_list容器

        本质及特点:优化list产生的,forward_list是单向链表,这样的话少存储一个指针,节省系统开销;

        ①只提供前向迭代器,不支持反向迭代器;

        ②不提供size()成员函数;

        ③没有指向最末元素的锚点,因此不提供back(),push_back(),pop_back();

        ④不提供随机访问,与list同;

        ⑤插入和删除元素不会造成“指向其他元素的”指针、引用、迭代器失效;

        5. deque 容器,本质及特点:

        ①deque是双端数组,而vector是单端的;

        ②与vector一样,可以随机存取元素;且在头部和尾部移除元素都非常快速,但是在中间插入元素则比较费时;

        ③其他内容以vector一样;

        6. set/multiset 容器  头文件:#include<set>

        (1)本质及特点

        ①set是一个集合容器,其中包含的元素是唯一的,集合中的元素按一定的顺序排列;元素插入过程是按排序规则插入,所以不能指定插入位置;

        ②set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树,在插入操作和删除操作上比vector快;时间复杂度是O(log n);

        ③set不可以直接存取元素(不可以使用at.(pos)与[ ] 操作符);

        ④multiset与set的区别是:set支持唯一键值,即每一个元素只能出现一次;而multiset中的元素可以出现多次;

        ⑤不可以直接修改set或multiset容器中的元素值,因为这类容器是自动排序的,如果要修改其中的某个元素值,必须先删除原有的元素,再插入新的元素;

程序示例:

#include <iostream>
#include <set>
#include <vector>

using namespace std;

template <typename T>
void print(const set<T> &other)
{
    for(auto it = other.begin(); it != other.end(); ++it)
    {
        cout<<*it<<" ";
    }
    cout<<endl;
}

int main(int argc, char **argv)
{
    set<int>si;
    si.insert(1);
    si.insert(3);
    si.insert(7);
    si.insert(5);
    si.insert(2);

    print(si);

    // auto node = si.extract(3);  //修改成员值,c++17,本系统不支持
    // node.value = 9;
    // si.insert(move(node));

    // set<int>s2 = {2,4,6,8};
    // si.merge(s2);  //将s2合并到si,c++17,本系统不支持

    cout<<"si.count(5) : "<<si.count(5)<<endl;  //数值是5的元素有几个?

    auto it = si.find(5);
    if(it != si.end())
    {
        cout<<"找到了"<< *it <<endl;
    }

    auto it1 = si.lower_bound(5); //返回首个不小于5的元素的迭代器
    auto it2 = si.upper_bound(5); //返回收个大于5的元素迭代器
    if(it1 != si.end())
    {
        cout<<"it1 = "<<*it1<<endl;
    }
    if(it2 != si.end())
    {
        cout<<"it2 = "<<*it2<<endl;
    }

    return 0;
}

        7. map/multimap 容器  #include<map>

        (1)本质及特点

        ①map是标准的关联式容器,一个map是一个键值对(一个序列号,一个值,相当于数组的索引和对应的值),它提供基于key(键值)的快速检索能力;键值对中第一个元素是first,第二个元素是second,支持迭代器访问输出;

        ②map中的key值是唯一的;集合中的元素按一定的顺序排列,元素插入过程是按排序规则插入,所以不能指定插入位置;

        ③map的具体实现采用红黑树变体的平衡二叉树数据结构,在插入和删除操作上比vector块;

        ④map可以直接存取key所对应的value,支持[ ] 操作符,如map[key] = value;

        ⑤multimap 与 map 的区别:map支持唯一键值,每个键只能出现一次,而multimap中相同的键可以出现多次。但是multimap不支持[ ]操作符;

        (2)示例

#include <iostream>
#include <map>
#include <string>

using namespace std;

template<typename key, typename value>
void print(const map<key, value> &other)
{
    for(auto it = other.begin(); it != other.end(); ++it)
    {
        cout<<"key : "<<it->first<<" value : "<<it->second<<endl;
    } 
}

int main(int argc, char **argv)
{
    map<int, string> m1;  //和数组很像,索引+对应的值
    m1[1] = "hello1";
    m1[2] = "hello2";
    m1[3] = "hello3";

    print(m1);

    return 0;
}

        (3)一个面试题示例:给定一个数组,计算每个数值出现的次数,然后按字符出现次数的升序输出数值及其出现次数;并且单独输出个数大于数组个数一半的元素;

#include <iostream>
#include <map>
#include <set>

using namespace std;

template<typename key, typename value>
void print(const multimap<key, value> &other)
{
    for(auto it = other.rbegin(); it != other.rend(); ++it)
    {
        cout<<"降序遍历 : 值"<<it->second<<"   次数:"<<it->first<<endl;
    } 
}

int main(int argc, char **argv)
{
    int num[]={1,3,3,3,5,3};
    int len  = sizeof(num)/(sizeof(num[0]));
    cout<<"len = "<<len<<endl;  //输出元素的个数
    map<int,int>mc;

    for(auto &temp : num)
    {
        mc[temp]++;
    }
    // cout<<"mc[3] = "<<mc[3]<<endl;  //输出数值3对应的次数

    multimap<int,int>mi;  //注意这里又新建了一个容器,在插入元素时,与mc相反,以次数为键值,这样就会自动根据次数排序,默认是升序,若要降序,则需要采用逆向迭代器
    for(auto it = mc.begin(); it != mc.end(); ++it)
    {
        mi.insert(pair<int,int>(it->second,it->first));
    }

    for(auto &temp : mi)
    {
        cout<<"升序遍历 : 值"<<temp.second<<"   次数:"<<temp.first<<endl;  //注意前后的输出顺序对照上
    }

    print(mi);
    
    for(auto &temp : mc)
    {
        if(temp.second > (len/2))
        {
            cout<<"符合要求:"<<temp.first<<" 出现次数:"<<temp.second<<endl;
        }
    }

    return 0;
}

结果:

        8. 容器的选择原则总结

        ①vector是内置有动态数组,当容量满时会自动扩充,在末尾有定位指针,所以末尾插入十分快速,但中间插入则较慢,因为涉及到原有数组的拷贝;最大的特点是适合高效的随机访问;而deque则优化为可以既可以头插也可以末尾插;

        ②list是一个双向链表容器,访问指定元素的效率较低,不支持随机访问,最大的特点是中间插入的效率较快;

        ③set是一个集合容器,元素具有唯一性,因为排序功能,所以开销较大;若需要支持重复数据则需要使用multiset;

        ④map是关联式容器,内置键值对,且内部按照顺序排序,内部采用红黑树的数据结构,时间复杂度是O(log n ),查找与删除的效率很高;适合字典式存储数据;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/613096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一分钟学一个 Linux 命令 - mkdir 和 touch

前言 大家好&#xff0c;我是god23bin。欢迎来到《一分钟学一个 Linux 命令》系列&#xff0c;今天需要你花两分钟时间来学习下&#xff0c;因为今天要讲的是两个命令&#xff0c;mkdir 和 touch 命令。前一个命令是操作目录的&#xff0c;后一个命令是操作文件的。 建议学完手…

蓝桥杯STC15F2K60S2单片机 CCP/PCA/PWM模块的学习与使用

有道是“一花独放不是春&#xff0c;万紫千红春满园” 我们不能只满足于 眼前所谓的 “够用、能用” 的少量知识&#xff0c;而不去深入学习探究&#xff0c;进而不慎封锁了自己的见识 和 更多 创新开发上的可能性。 曾经仅满足于学习了蓝桥杯单片机的三个外部晶振 定时器&am…

日撸java三百行day58-59

文章目录 说明Day58 符号型数据的 NB 算法1.基础理论知识1.1 条件概率1.2 独立性假设1.3 Laplacian 平滑 2. 符号型数据的预测算法跟踪2.1 testNominal()方法2.1.1 NaiveBayes 构造函数2.1.2 calculateClassDistribution()2.1.3 calculateConditionalProbabilities()方法2.1.4 …

D*算法详解 (D星算法 / Dynamic A*算法/ Dstar算法)(死循环解决)【编辑中】

所需先验知识&#xff08;没有先验知识可能会有大碍&#xff0c;了解的话会对D*的理解有帮助&#xff09;&#xff1a;A*算法/ Dijkstra算法 何为D*算法 Dijkstra算法是无启发的寻找图中两节点的最短连接路径的算法&#xff0c;A*算法则是在Dijkstra算法的基础上加入了启发函数…

【linux下一次复制cp多个文件】

linux下一次复制cp多个文件 linux cp 参数说明 -a&#xff1a;此选项通常在复制目录时使用&#xff0c;它保留链接、文件属性&#xff0c;并复制目录下的所有内容。其作用等于dpR参数组合。 -d&#xff1a;复制时保留链接。这里所说的链接相当于Windows系统中的快捷方式。 -f&…

使用可上网的服务器作为网关,实现另一台服务器上网

文章目录 物理条件方法一&#xff1a;不使用交换机方法二&#xff1a;使用交换机 配置步骤查看网络状态设置静态IP将服务器A设置成网关重新启动各服务器网卡设置主机名 参考资料 本文的目标是利用一台可以上网的服务器作为网关&#xff0c;使在同一局域网中的所有服务器都可以上…

如何安装多个node版本(不使用nvm)

1、选择node安装的路径 例如&#xff1a;D:\Program Files\nodejs 2、下载要安装的对应版本的zip格式的node安装包 例如&#xff1a;下载16.14.2 和 13.14.0 两个版本的zip格式的node安装包&#xff0c;node版本下载地址https://nodejs.org/dist/ 3、安装包解压到对应文件夹…

遥感云大数据在灾害、水体与湿地领域典型

近年来遥感技术得到了突飞猛进的发展&#xff0c;航天、航空、临近空间等多遥感平台不断增加&#xff0c;数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量猛增&#xff0c;遥感数据已经越来越具有大数据特征。遥感大数据的出现为相关研究提供了前所未有的机遇&#xf…

路径规划算法:基于JAYA优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于JAYA优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于JAYA优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法JAYA…

深入理解深度学习——正则化(Regularization):对抗训练(Adversarial Training)

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 在许多情况下&#xff0c;神经网络在独立同分布的测试集上进行评估已经达到了人类表现。因此&#xff0c;我们自然要怀疑这些模型在这些任务上是否获得了真正的人类层次的理解。为了探索网络对底层任务的理解层次&…

如何理解并使用 park 与 unpark

tip: 作为程序员一定学习编程之道&#xff0c;一定要对代码的编写有追求&#xff0c;不能实现就完事了。我们应该让自己写的代码更加优雅&#xff0c;即使这会费时费力。 文章目录 一、基本介绍二、使用例子三、使用注意事项 一、基本介绍 park和unpark是Java中的两个线程同步…

高性能软件负载OpenResty介绍和安装

目录 1 OpenResty介绍1.1 Nginx 的流程定义1.1.1 流程详解1.1.2 OpenResty处理流程 2 Openresty安装2.1 yum安装2.1.1 添加OpenResty仓库2.1.2 安装OpenResty 2.2 源代码编译安装2.2.1 安装编译环境2.2.2下载最新版源码2.2.3下载缓存插件2.2.4 编译OpenResty2.2.5 安装OpenRest…

《设计模式》之状态模式

文章目录 1、定义2、动机3、类结构4、优点5、总结6、代码实现(C) 1、定义 允许一个对象在其内部改变时改变它的行为&#xff0c;从而使对象看起来改变了其行为。 2、动机 某些对象的状态如果改变&#xff0c;其行为也会随之而发生改变。比如文档的只读状态和读写状态的行为可…

【MySQL】- 04 MVCC实现原理

MVCC的实现原理 隐式字段undo日志Read View(读视图)整体流程例子 MVCC的目的就是多版本并发控制&#xff0c;在数据库中的实现&#xff0c;就是为了解决读写冲突&#xff0c;它的实现原理主要是依赖记录中的 3个隐式字段&#xff0c;undo日志 &#xff0c;Read View 来实现的。…

Linux笔记-so.1和so的区别(三方程序链接Qt的so.1和so)

如下面这个程序使用ldd查看下&#xff1a; 从中可以看到一些so.6和so.1这些&#xff0c;这些其实是版本号&#xff0c;比如这个&#xff1a;/lib64/libstdc.so.6 可见so.版本号其实是个软连接&#xff0c;最终会连接到/lib64/libstdc.so.6.0.19 而CQt框架写的lib如下pro文件会生…

磁盘配额与进阶文件系统管理(一)

磁盘配额Quota 用途 针对www server&#xff0c;例如 每个人网页空间的容量限制&#xff1b;针对mail server&#xff0c;例如 每个人的邮件空间限制&#xff1b;针对file server&#xff0c;例如 每个人最大可用的网络硬盘空间&#xff1b;限制某一群组所能使用的最大磁盘空…

使用zerotier进行内网穿透,外网访问其它电脑上的虚拟机

目标 使用一台电脑&#xff0c;使用vmware创建三台虚拟机&#xff0c;处于同一网段。另一台电脑外网进行访问其虚拟机 用途 学习K8s集群&#xff0c;由于个人财力有限&#xff0c;云服务器买不了几台&#xff0c;而且不同厂家的云服务器无法做到内网互通 完成后缺陷 使用z…

nginx系统优化和内核优化

nginx系统优化 一&#xff1a;隐藏nginx版本号 方法一&#xff1a;修改配置文件 vim /usr/local/nginx/conf/nginx.confnginx -t systemctl restart nginx curl -I http://192.168.52.108方法二&#xff1a;修改源代码 vim /opt/nginx-1.24.0/src/core/nginx.h ##配置文件里…

逆向汇编与反汇编——汇编基础快速入门

一、常用32位寄存器介绍 不同位数的寄存器的名称&#xff1a; eax&#xff1a;累加寄存器。通常用于算数运算&#xff0c;将结果保留在eax当中&#xff0c;当然也可以用于其他用途&#xff0c;比如一般把返回值通过eax传递出去。 ebx&#xff1a;基址寄存器 。有点类似于ebp…

Go语言日志库logrus

Go语言日志库logrus 1、介绍 logrus logrus是目前Github上star数量最多的日志包&#xff0c;功能强大、性能高效、高度灵活&#xff0c;还提供了自定义插件的功能。很 多优秀的开源项目&#xff0c;例如&#xff1a;docker、prometheus等都使用了logrus。logrus除了具有日志…