C++ STL学习总结

C++ STL的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器负责存储数据,算法负责计算结果,而算法调用容器的数据必须通过迭代器去访问,而每一种容器都提供了一种访问该容器中数据的迭代器。STL主要提供有两类容器跟容器适配器,两类容器分别为:序列式容器<向量(vector)、双端队列(deque)、链表(list)>以及关联式容器<集合(set(multiset))及映射(map(multimap))>;容器适配器为:栈(stack)和队列(queue),容器的示意图如下图所示:

基本接口

vector

特点:
 (1)拥有一段连续的内存空间,因此它能非常好的支持随机访问,即 [] 操作符和 .at(),随机访问快。(优点)
 (2)当向其头部或中间插入或删除元素时,为了保持原本的相对次序,插入或删除点之后的所有元素都必须移动,所以插入或删除的效率比较低。(缺点)
 (3)在后面插入删除元素最快,此时一般不需要移动内存。(优点)
总结:相当于可拓展的数组(动态数组),随机访问快,在头部和中间插入或删除效率低,但在尾部插入或删除效率高。

基本操作:
    v.empty();        // 判断容器是否为空
    v.capacity();    // 容器容量
    v.size();        // 容器大小
    v.at(int idx);    // 用法和[]运算符相同
    v.front();        // 获取头部元素
    v.back();        // 获取尾部元素
    v.begin();        // 头元素的迭代器
    v.end();        // 尾部元素的迭代器
    v.push_back(elem);    // 尾部插入
    v.pop_back();    // 尾部删除
    v.insert(pos, elem);    //在位置pos插入elem元素
    v.insert(pos, n, elem);    //在位置pos上插入n个元素elem
    v.insert(pos, begin, end);    //在位置pos上插入begin至end区间的数据
    v.erase(pos);            //移除pos位置上的元素,返回下一个数据的位置
    v.erase(begin, end);    //移除[begin, end)区间的数据,返回下一个元素的位置
    v.clear();        // 清空元素
    v.swap(vec);    // 将vec与自身元素互换
    v.reseve(int len);    //预留空间,由于每次往vector中添加元素时都会检查空间是否够用,不够就会重新换一块大的内存,这样耗时耗力。

deque

特点:
(1)一旦要在 deque 的头部和尾部增加新空间,便配置一段定量连续空间,串在整个 deque 的头部或尾部,因此不论在头部或尾部插入元素都十分迅速。 (优点)
(2)在中间部分安插元素则比较费时,因为必须移动其它元素。(缺点)
(3)deque 是 list 和 vector 的折中方案。兼有 list 的优点,也有 vector 随机访问效率高的优点。
总结:支持随机访问,但效率没有 vector 高,在头部和尾部插入或删除效率高,但在中间插入或删除效率低。

基本操作:deque几乎支持vector所以的操作,但由于daque是双向队列可以双向插入跟删除,所以都有自己独有的两个操作如下:
    d.push_front(elem);    //头部插入
    d.pop_front();    //头部删除

list

特点:
(1)内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随机存取变得非常没有效率,因此它没有提供 [] 操作符的重载。(缺点)
(2)由于链表的特点,在任意位置的插入和删除效率都较高。(优点)
(3)只支持首尾两个元素的直接存取,想获取其他元素(访问时间一样),则需要遍历链表。(缺点)
总结:不支持随机访问,在任意位置的插入和删除效率都较高。

基本操作:
    l.size();      // 链表大小
    l.empty();        // 判断链表是否为空
    l.resize(num);    // 重新制定链表长度,若长度比之前大,则用默认值填充,若小于之前大小,则超出的元素被删除
    l.resize(num, elem);        // 指定值填充
    l.front();                    // 返回第一个元素
    l.back();                    // 返回最后一个元素
    l.begin();                    // 头元素的迭代器
    l.end();                    // 尾部元素的迭代器
    l.push_back(elem);             // 尾部插入
    l.pop_back();                  // 尾部删除
    l.push_front(elem);            // 头部插入
    l.pop_front();                // 头部删除
    l.insert(pos,elem);            // 在位置pos插入elem元素
    l.insert(pos, n, elem)        // 在位置pos上插入n个元素elem
    l.insert(pos, begin, end);    // 在位置pos上插入begin至end区间的数据
    l.erase(pos);                  // 移除pos位置上的元素,返回下一个数据的位置
    l.erase(begin, end);        // 移除[begin, end)区间的数据,返回下一个元素的位置
    l.clear();            // 清空元素
    l.remove(elem);        // 删除容器中所有与elem匹配的元素
    l.swap(lst);        // 将lst与自身元素互换
    l.reverse();        // 链表反转
    l.sort();            // 链表排序,默认升序,如果不为升序许自己实现排序规则

注:不支持随机访问的迭代器的容器不可以用标准算法,其内部会提供对应的一些算法(比如排序算法)。

set(multiset) 

特点:
(1)使用红黑树实现,其内部元素依据其值自动排序,set中每个元素值只能出现一次,不允许重复,multiset中元素可以重复。
(2)每次插入值的时候,都需要调整红黑树,效率有一定影响。(缺点)
(3)map(multimap)  和 set(multiset)  的插入或删除效率比用其他序列容器高,因为对于关联容器来说,不需要做内存拷贝和内存移动。(优点)
总结:由红黑树实现,其内部元素依据其值自动排序,且插入和删除效率比用其他序列容器高。

基本操作:
    s.size();      // 容器大小
    s.empty();        // 判断容器是否为空
    s.find(key);                // 返回元素的迭代器
    s.count(key);                // 返回元素的个数
    s.insert(elem);                // 插入elem元素
    s.erase(pos);                  // 移除pos迭代器所指的位置
    s.erase(begin, end);        // 移除[begin, end)区间的数据,返回下一个元素的位置
    s.erase(elem);              // 删除容器中所有与elem匹配的元素
    s.clear();            // 清空元素
    s.swap(set);        // 将set与自身元素互换

map(multimap) 

特点:
(1)map中每个元素都有一个键,且只能出现一次,不允许重复。而multimap中可以出现重复键值。
(2根据 key 值快速查找记录,查找的复杂度基本是 O(logN),如果有 1000 个记录,二分查找最多查找 10次(1024)。(优点)
(3)每次插入值的时候,都需要调整红黑树,效率有一定影响。(缺点)
(4)增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。(优点)
(5)对于迭代器来说,可以修改实值,而不能修改 key。
总结:元素为键值对,key 和 value 可以是任意你需要的类型,根据 key 快速查找记录。 

基本操作:
    m.size();                // 容器大小
    m.empty();                // 判断容器是否为空
    m.find(key);            // 返回元素的迭代器
    m.count(key);            // 返回元素的个数
    m.insert(elem);            // 插入elem元素
    m.erase(pos);              // 移除pos迭代器所指的位置
    m.erase(begin, end);     // 移除[begin, end)区间的数据,返回下一个元素的位置
    m.erase(elem);             // 删除容器中所有与elem匹配的元素
    m.clear();              // 清空元素
    m.swap(set);            // 将set与自身元素互换

基本操作案例

将STL的容器及其常用的算法做了如下案例测试,具体代码如下所示:

/**************************************
作   者 : lijd
生成日期 : 2021年03月02日
功能描述 : C++中STL容器、算法基本操作测试
**************************************/ 
#include <iostream>
#include <string>
using namespace std;

#include <vector>
#include <list>
#include <set>
#include <map>
#include <algorithm>
#include <numeric>
#include <functional>
#include <ctime>

// 自定义函数输出
void lijd_print(int val)
{
    cout<< val <<" ";
}

// 仿函数类模板打印输出
template <class T>
class MyPrint
{
public:
    void operator()(T val)
    {
	cout<< val <<" ";
    }
};

void test01()
{
    cout<<"----------------------------------------test 01--------------------------------------"<<endl;
    vector<int>	v1;
    // 生产随机种子
    srand((unsigned int)time(NULL));

    for(int i = 0; i < 10; i++)
    {
	v1.push_back(rand()%50);
    }
    cout << "排序前:" << endl;
    // 调用用户定义函数打印
    for_each(v1.begin(), v1.end(), lijd_print);
    cout << endl;

    cout<<"集合中元素的总和为:"<<accumulate(v1.begin(), v1.end(), 0/*基础累加值*/)<<endl;

    // sort默认从小到大排序,调用了关系仿函数 template <class T> less<T>
    sort(v1.begin(), v1.end());
    cout << "从小到大排序后:" << endl;
    // 调用用户自定义仿函数打印
    for_each(v1.begin(), v1.end(), MyPrint<int>());
    cout << endl;

    // 1、sort的最后一个参数是谓词(返回值为bool的仿函数),下面例子为其创建一个模板类实例化的匿名对象
    // 2、functional提供了一堆基于模板的比较函数对象:equal_to<Type>、not_equal_to<Type>、
    // greater<Type>、greater_equal<Type>、less<Type>、less_equal<Type>
    sort(v1.begin(), v1.end(), greater<int>());
	
    cout << "从大到小排序后:" << endl;
    for_each(v1.begin(), v1.end(), MyPrint<int>());
    cout << endl;
}

class Person
{
    friend ostream& operator<<(ostream &p_cout, Person &p);
public:
    Person(string name, int age):s_name(name), s_age(age){};

    int GetAge(){return this->s_age;};

    // 排序的时候需要调用my_sort_desc仿函数及list自带的sort函数,两者都需要重载 <
    // functional提供的模板类的谓词必须为const函数
    bool operator<(const Person &p) const
    {
	return (this->s_age < p.s_age ? true : false);
    };

    // 排序时调用list自带的sort函数时参数为谓词greater需要重载 >;
    bool operator>(const Person &p) const
    {
	return (this->s_age > p.s_age ? true : false);
    };

    // set集合调用find查找时需要重载==
    bool operator==(const Person &p) const
    {
	return (this->s_age == p.s_age ? true : false);
    }

private:
    string s_name;
    int s_age;
};

// 输出需要调用MyPrint仿函数,需重载"<<"
ostream& operator<<(ostream &p_cout, Person &p)
{
    p_cout<<"name:"<<p.s_name<<" age:"<<p.s_age<<"; ";
    return p_cout;
};

// 仿函数类模板年龄从大到小
template <class T>
class my_sort_desc
{
public:
    bool operator()(T val1, T val2) const
    {
	return val1 > val2;
    }
};

// 仿函数类模年龄从大到小
class age_great_num
{
public:
    age_great_num(int num):s_num(num){};
    bool operator()(Person &p) const
    {
	return p.GetAge() > this->s_num;
    }
private:
    int s_num;
};

void test02()
{
    cout<<"----------------------------------------test 02--------------------------------------"<<endl;
    cout<<"---------------------vector 测试---------------------"<<endl;
    vector<Person> v1;
    v1.push_back(Person("唐僧", 30));
    v1.push_back(Person("孙悟空", 1000));
    v1.push_back(Person("猪八戒", 900));
    v1.push_back(Person("沙和尚", 800));

    cout<<"vector 排序前:"<<endl;
    for_each(v1.begin(), v1.end(), MyPrint<Person>());
    cout<<endl;

    cout<<"vector 从小到大排序后:"<<endl;
    // 调用系统自带的less<T>模板类
    sort(v1.begin(), v1.end(), less<Person>());
    for_each(v1.begin(), v1.end(), MyPrint<Person>());
    cout<<endl;
    cout<<"---------------------list 测试---------------------"<<endl;

    // 注:不支持随机访问的迭代器的容器不可以用标准算法,其内部会提供对应的一些算法(比如排序算法)
    list<Person> lst;
    lst.push_back(Person("唐僧", 30));
    lst.push_back(Person("孙悟空", 1000));
    lst.push_back(Person("猪八戒", 900));
    lst.push_back(Person("沙和尚", 800));

    cout<<"list 排序前:"<<endl;
    for_each(lst.begin(), lst.end(), MyPrint<Person>());
    cout<<endl;

    // list需要调用自带sort排序,默认调用less仿函数,该参数调用自己实现的my_sort_desc<T>模板类
    lst.sort(my_sort_desc<Person>());

    cout<<"list 从大到小排序后:"<<endl;
    for_each(lst.begin(), lst.end(), MyPrint<Person>());
    cout<<endl;
}

void test03()
{
    cout<<"----------------------------------------test 03--------------------------------------"<<endl;
    set<int> s1;	// 如果为内置数据类型,默认有序,从小到大
	
    // 生产随机种子
    srand((unsigned int)time(NULL));

    for(int i = 0; i < 10; i++)
    {
	s1.insert(rand()%50);
    }
    cout<<"set 插入内置数据类型从小到大:"<<endl;
    for_each(s1.begin(), s1.end(), MyPrint<int>());
    cout<<endl;

    // 此处定义multiset自定义数据类型,年龄从大到小;尝试过用set年龄相同的会去重!
    multiset<Person, greater<Person>> myset;	
    myset.insert(Person("唐僧", 30));
    myset.insert(Person("孙悟空", 1000));
    myset.insert(Person("猪八戒", 900));
    myset.insert(Person("沙和尚", 800));
    myset.insert(Person("红孩儿", 500));
    myset.insert(Person("牛魔王", 1200));
    myset.insert(Person("白骨精", 800));
    myset.insert(Person("玉皇大帝", 1000));

    cout<<"set 插入自定义数据类型年龄从大到小:"<<endl;
    for_each(myset.begin(), myset.end(), MyPrint<Person>());
    cout<<endl;

    multiset<Person, greater<Person>>::iterator it = find(myset.begin(), myset.end(), Person("二郎神", 900));
    if(it != myset.end())
    {
	cout<<"find 查找到与二郎神年龄相同的为:"<<*it<<endl;
    }
	
    // 最后一个参数为其创建一个实例化的匿名对象
    it = find_if(myset.begin(), myset.end(), age_great_num(Person("王母娘娘", 500).GetAge()));
    if(it != myset.end())
    {
	cout<<"find_if 查找第一个年龄大于王母娘娘的为:"<<*it<<endl;
    }

    int num = count(myset.begin(), myset.end(), Person("太白金星", 800));
    cout<<"count 查找与太白金星年龄相同的个数有:"<<num<<endl;

    num = count_if(myset.begin(), myset.end(), age_great_num(Person("太白金星", 800).GetAge()));
	cout<<"count 查找比太白金星年龄大的个数有:"<<num<<endl;

    // 生产随机种子
    srand((unsigned int)time(NULL));

    // 定义key值从大到小的map对象
    map<int, Person, greater<int>> m1;
    m1.insert(make_pair(rand()%50, Person("唐僧", 30)));
    m1.insert(make_pair(rand()%50, Person("孙悟空", 1000)));
    m1.insert(make_pair(rand()%50, Person("猪八戒", 900)));
    m1.insert(make_pair(rand()%50, Person("沙和尚", 800)));

    cout<<"map m1的元素遍历为:"<<endl;
    map<int, Person, greater<int>>::iterator it1;
    for(it1 = m1.begin(); it1 != m1.end(); it1++)
    {
	cout<<"key: "<<it1->first<<" value: ["<<it1->second<<"]"<<endl;
    }

    map<int, Person> m2;
    m2.insert(make_pair(rand()%50, Person("红孩儿", 500)));
    m2.insert(make_pair(rand()%50, Person("牛魔王", 1200)));
    m2.insert(make_pair(rand()%50, Person("白骨精", 800)));
    m2.insert(make_pair(rand()%50, Person("玉皇大帝", 1000)));

    cout<<"map m2的元素遍历为:"<<endl;
    map<int, Person>::iterator it2;
    for(it2 = m2.begin(); it2 != m2.end(); it2++)
    {
	cout<<"key: "<<it2->first<<" value: ["<<it2->second<<"]"<<endl;
    }
}

void test04()
{
    cout<<"----------------------------------------test 04--------------------------------------"<<endl;

    vector<int> v1, v2, vtag;
    for(int i = 0; i < 10; i++)
    {
	v1.push_back(i);
	v2.push_back(i+5);
    }

    cout<<"v1 数据元素:"<<endl;
    for_each(v1.begin(), v1.end(), MyPrint<int>());
    cout<<endl;

    cout<<"v2 数据元素:"<<endl;    
    for_each(v2.begin(), v2.end(), MyPrint<int>());
    cout<<endl;

    // 目标集合申请空间    
    vtag.resize(min(v1.size(), v2.size()));
    vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vtag.begin());
    	
    cout<<"v1 和 v2 集合的交集为:"<<endl;
    for_each(vtag.begin(), itEnd, MyPrint<int>());
    cout<<endl;

    vtag.resize(v1.size() + v2.size());
    itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vtag.begin());

    cout<<"v1 和 v2 集合的并集为:"<<endl;
    for_each(vtag.begin(), itEnd, MyPrint<int>());
    cout<<endl;

    vtag.resize(v1.size());
    itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vtag.begin());

    cout<<"v1 和 v2 集合的非集为(v1中存在v2不存在):"<<endl;
    for_each(vtag.begin(), itEnd, MyPrint<int>());
    cout<<endl;

    swap(v1, v2);
    cout<<"交换后 :"<<endl;
    cout<<"v1 数据元素:"<<endl;
    for_each(v1.begin(), v1.end(), MyPrint<int>());
    cout<<endl;

    cout<<"v2 数据元素:"<<endl;
    for_each(v2.begin(), v2.end(), MyPrint<int>());
    cout<<endl;
}

int main()
{
    test01();
    test02();
    test03();
    test04();

    system("pause");
    return 0;
}

运行结果如下图:

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值