c++ STL相关内容,读完文章可以对STL有清晰的了解

声明:其中所有内容是自己在学习过程中所作的笔记,部分内容是来源于网络资源和c++ primer,也是自己辛苦一字一字打出来的,其它内容在之后进行补充。(使用的是obsidian编辑器,是一款开源的markdown编辑器,感兴趣可以了解下载使用)。

STL相关

  • STL(Standard Template Library),标准模板库
  • STL从广义上分为:容器(container), 算法(algorithm), 迭代器(iterator)
  • 容器和算法之间通过迭代器进行无缝连接
  • STL几乎所有代码都采用模板类或模板函数
  • 通常不对关联容器使用泛型算法。关键字是const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,而set类型中的元素是const的,map中的元素是pair,其第一个成员是const的。
    [[string]] [[总]] [[deque]] [[queue]] [[stack]] [[list]] [[set multiset]] [[mat multimap]]

STL 六大组件

分别为:容器,算法,迭代器,仿函数,适配器(配接器), 空间配置器

  1. 容器:各种数据结构如:vector,list,deque,set,map等。用来存放数据
  2. 算法:各种常用算法如:sort,find,copy,for_each等
  3. 迭代器:扮演了容器和算法之间的胶合剂
  4. 仿函数:行为类似函数,可作为算法的某种策略 (重载())[[c++类相关#仿函数]]
  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
  6. 空间配置器:负责空间的配置和管理

容器,算法,迭代器

容器:置物之所也

STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组, 链表,树, 栈, 队列, 集合, 映射表 等
这些容器分为序列式容器关联式容器两种:

  • 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
  • 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

算法:问题之解法也

  • 有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
    算法分为:质变算法非质变算法
  • 质变算法:是指运算过程中会更改区间内的元素的内容。例如:拷贝,替换,删除等等
  • 非质变算法:是指运算过程中不会更改区间内的元素内容,例如:查找、计数、遍历、寻找极值等等

迭代器:容器和算法之间粘合剂

  • 提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
  • 每个容器都有自己专属的迭代器
  • 迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针

存放内置数据类型

#include <vector>
#include <algorithm>
void test01()
{
	vector<int> vec;
	for (int i = 0; i < 5; i++)
		vec.push_back(i)  // 尾插法
	// c++11 新语法
	for (const auto &v : vec)
		cout << v << endl;
	// 迭代器
	vector<int>::iterator it = vec.begin();
	vector<int>::const_iterator it = vec.cend();
	// c++11 新
	auto it = vec.begin();
}

存放自定义数据类型

  • 传入的对象是什么类型那么迭代器所指向的就是什么
void test01()
{
	vector<Person*> vec;
	// 此时 v 为vector所传入的类型 即 Person*
	for (const auto &v: vec)
		cout << vec-> << " " << vec-> << endl;
}

容器嵌套容器

void test01()
{
    vector<vector<int>> vec;
    vector<int> v1;
    vector<int> v2;
    vector<int> v3;
    for (int i = 0; i < 5; i++)
    {
        v1.push_back(i + 1);
        v2.push_back(i + 2);
        v3.push_back(i + 3);
    }
    vec.push_back(v1);
    vec.push_back(v2);
    vec.push_back(v3);
    //for (auto it = vec.begin(); it != vec.end(); it++)
    //{
    //    for (auto vit = it->begin(); vit != it->end(); vit++)
    //        cout << *vit << " ";
    //}
    for (const auto &v1 : vec)
    {
        for (const auto &v2: v1)
            cout << v2 << " ";
        cout << endl;
    }
}

vector

  • vector与普通数组的区别:数组不可以动态扩展,vector可
  • vector的数据结构和数组类似,都是单端数组

动态扩展

  • 并不是在原空间之后续接新空间,而是找更大的一块空间,然后拷贝原数据到新空间,释放原空间
  • vector的迭代器支持随机访问

构造函数

函数原型

  • vector<T> v;
  • vector(v.begin(), v.end());
  • vector(n, elem);
  • vector(const vector &vec);
void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);
    printV(v1); 

    vector<int> v2(v1.begin(), v1.end());
    printV(v2);

    vector<int> v3(10, 100);
    printV(v3);

    vector<int> v4(v3);
    printV(v4);
}

赋值操作

函数原型

  • vector& operator=(const vector &vec);
  • assign(beg, end);
  • assign(n, elem);
void test02()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);

    vector<int> v2;
    v2 = v1;
    printV(v2);

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

    vector<int> v4;
    v4.assign(10, 100);
    printV(v4);
}

容量和大小

函数原型

  • empty();
  • capacity();
  • size();
  • resize(int num); 重新指定容器的长度,若容器变长则以默认值填充新位置,如果容器变短则超出部分被删除
  • resize(int num, elem); 以elem填充新位置
  • shrink_to_fit(); 将 capacity 减为和 size 的同样大小
void test03()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);
    if (!v1.empty())
    {
        cout << "capacity: " << v1.capacity() << endl;
        cout << "size: " << v1.size() << endl;
    }

    v1.resize(20);
    printV(v1);

    v1.resize(14, 10);
    printV(v1);

    v1.resize(5);
    printV(v1);
}

插入和删除

函数原型

  • push_back(ele);
  • pop_back();
  • insert(const_iterator pos, ele);
  • inster(const_iterator pos, int count, ele);
  • erase(const_iterator pos);
  • erase(const_iterator start, const_iterator end);
  • clear();
void test04()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);

    printV(v1);
    v1.pop_back();
    printV(v1);

    //插入
    v1.insert(v1.begin(), 100);
    printV(v1);

    v1.insert(v1.begin(), 2, 1000);
    printV(v1);

    //删除
    v1.erase(v1.begin());
    printV(v1);

    v1.erase(v1.begin(), v1.end());
    printV(v1);

    v1.clear();
    printV(v1);
}

数据存取

函数原型

  • at(int idx);
  • operator[];
  • front();
  • back();
void test05()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);
    for (int i = 0; i < v1.size(); ++i)
        cout << v1[i] << " ";
    cout << endl;
    
    for (int i = 0; i < v1.size(); ++i)
        cout << v1.at(i) << " ";
    cout << endl;

    auto one = v1.front();
    auto end = v1.back();
    cout << one << " " << end << endl;
}

互换容器

两个容器的元素互换

函数原型

  • swap(vec);
// swap 实际用途
void test07()
{
    vector<int> v1; 
    for (int i = 0; i < 100000; i++)
        v1.push_back(i);
    cout << "capacity: " << v1.capacity() << endl; // 131072
    cout << "size: " << v1.size() << endl;         // 100000

    v1.resize(3);
    cout << "capacity: " << v1.capacity() << endl; // 131072
    cout << "size: " << v1.size() << endl;         // 3

    // 巧用swap收缩内存
    vector<int> (v1).swap(v1); // vector<int> (v1) 匿名对象 Person (p);
    cout << "capacity: " << v1.capacity() << endl; // 3
    cout << "size: " << v1.size() << endl;         // 3
}

预留空间

减少vector在动态扩展容量时的扩展次数

函数原型

  • reserve(int len); 容器预留len个元素长度,预留位置不初始化,元素不可访问
void test08()
{
    vector<int> v1;
    // 预留100000 的空间
    v1.reserve(100000);
    int num = 0;
    int *p = nullptr;
    for (int i = 0; i < 100000; i++)
    {
        v1.push_back(i);
        if (p != &v1[0])
        {
            p = &v1[0];
            num++;
        }
    }
    cout << "num: " << num << endl;  // 18 相当于分配了18次内存
}

string

本质

  • string是c++风格的字符串,本质上是一个类
    string和char *的区别
  • char * 是一个指针
  • string 是一个类,内部封装了 char * ,管理这个字符串,是一个char* 型容器
    特点
  • string 类封装了,find,copy,delete,replace,insert等方法
  • string 管理 char* 所分配的内存,不用担心越界和取值越界等问题

string 构造函数

构造函数原型

  • string();
  • string(const char *s);
  • string(const string &str);
  • string(int n, char c);
void test01()
{
    string s1;
    const char *str = "hello world";
    string s2(str);
    cout << s2 << endl;

    string s3(s2);
    cout << "s3: " << s3 << endl;

    string s4(10, 'a');
    cout << "s4: " << s4 << endl;
}

string 赋值操作

赋值的函数原型

  • string& operator=(const char *s);
  • string& operator=(const string &s);
  • string& operator=(char c);
  • string& assign(const char *s);
  • string& assign(const char *s, int n);
  • string& assign(const string &s);
  • string& assign(int n, char c);
void test02()
{
    string str1;
    str1 = "wuyan";

    string str2;
    str2 = str1;

    string str3;
    str3 = 'a';

    string str4;
    str4.assign("wuyan");

    string str5;
    str5.assign("wuyan12345", 5); // out put wuyan

    string str6;
    str6.assign(str5);

    string str7;
    str7.assign(10, 'w');
}

string 字符串拼接

函数原型

  • string& operator+=(const char *str);
  • string& operator+=(const char c);
  • string& operator+=(const string &str);
  • string& append(const char *s);
  • string& append(const char *s, int n);
  • string& append(const string &s);
  • string& append(const string &s, int pos, int n);
void test03()
{
    string str1("w");
    str1 += "uyan";
    
    str1 += '!';

    string str2(" it`s so cool");
    str1 += str2;

    string str3("I");
    str3.append(" love ");
    // out put I love you
    str3.append("you 12313", 3);
    str3.append(str2);
    str3.append(str2, 0, 3);
}

string 查找和替换

函数原型

  • int find(const string &str, int pos = 0) const;
  • int find(const char *, int pos = 0) const;
  • int find(const char *, int pos = 0,int n) const;
  • int find(const char c, int pos = 0) const;
  • int rfind(const string &str, int pos = npos) const;
  • int rfind(const char *, int pos = npos) const;
  • int rfind(const char *, int pos, int n) const;
  • int rfind(const char c, int pos = 0) const;
  • string& replace(int pos, int n, const string &str);
  • string& replace(int pos, int n, const char *);
void test04()
{
    // 查找
    string str1 = "abcdefgde";
    // 返回下标,找到返回下标,未找到返回-1
    int pos = str1.find("df");
    // rfind 从后往左查
    pos = str1.rfind("de");

    // 替换
    string str2 = "abcdefg";
    str2.replace(1, 3, "1111"); // output a1111efg
}

string 字符串比较

  • 字符串是根据ASCII码进行对比
    函数原型
  • int compare(const string &s) const;
  • int compare(const char *s) const;
void test05()
{
    string str1 = "xuyan";
    string str2 = "wuyan";
    if (str1.compare(str2) == 0)
        cout << "相等" << endl; 
    else if (str1.compare(str2) > 0)
        cout << "大于" << endl;
    else
        cout << "小于" << endl;
}

string字符串存取

  • string 中 单个字符存取有两种
    • char& operator[](int n);
    • char& at(int n);
void test06()
{
    string str1 = "wuyan";
    for (int i = 0; i < str1.size(); i++)
        cout << str1[i] << endl;
    for (int i = 0; i < str1.size(); i++)
        cout << str1.at(i) << endl;
    str1[0] = 'x';
    str1.at(1) = 'x';
}

string 中的插入和删除

函数原型

  • string& insert(int pos, const char * s);
  • string& insert(int pos, const string &str);
  • string& insert(int pos, int n, char c);
  • string& erase(int pos, int n = npos)
void test07()
{
    string str = "hello";
    str.insert(1, "111");  // h111ello
    cout << str << endl;

    str.erase(1, 3);
    cout << str << endl;  // hello
}

string 子串

函数原型

  • string substr(int pos = 0, int n = npos) const;
void test08()
{
    string email = "wuyan@qq.com";
    int pos = email.find('@');
    string subemial = email.substr(0, pos);
    cout << subemial << endl;  // wuyan
}

stack

  • stack是一种先进后出(First in last out, FILO)的数据结构,它只有一个出口
  • 只有栈顶可以访问,因此不支持遍历

常用接口

  • 构造函数
    • stack<T> stk;
    • stack(const stack &stk);
  • 赋值操作
    • stack& operator=(const stack &stk);
  • 数据存取
    • push(elem);
    • pop();
    • top();
  • 大小操作
    • empty();
    • size();
void test01()
{
    stack<int> st;
    for (int i = 0; i < 10; i++)
        st.push(i);
    if (!st.empty())
        cout << "size: " << st.size() << endl;
    while(!st.empty())
    {
        cout << "st.top(): " << st.top() << endl;
        st.pop(); //出栈
    }
    cout << "size: " << st.size() << endl;  // 0
}

set multiset

  • 所有元素都会在插入时自动排序
  • set和multiset属于关联式容器,底层结构是二叉树实现的
  • set类型同时定义了iterator和const_iterator类型,但两种类型都只允许只读访问set中的元素
  • 通常不对关联容器使用泛型算法(参见第10章)。关键字是const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,而set类型中的元素是const的,map中的元素是pair,其第一个成员是const的。

区别

  • set 不允许容器中有重复的元素
  • multiset 反之

构造函数

函数原型

  • set<T> st;
  • set(const set &t);
  • set& operator(const set &t);
void test01()
{
    set<int> st;
    st.insert(10);
    st.insert(20);
    st.insert(50);
    st.insert(40);
    st.insert(30);
    st.insert(30);

    set<int> st2(st);

    set<int> st3;
    st3 = st2;
}

大小和交换

函数原型

  • size();
  • empty();
  • swap(st);
void test02()
{
    set<int> st;
    st.insert(10);
    st.insert(20);
    st.insert(50);
    st.insert(40);
    st.insert(30);
    st.insert(30);
    
    if (!st.empty())
        cout << "size: " << st.size() << endl;
    set<int> st2;
    st2.insert(10);
    st.swap(st2);
    printS(st);
    printS(st2);
}

插入和删除

构造函数

  • insert(elem);
  • clear();
  • erase(pos);
  • erase(beg, end);
  • erase(elem);
void test03()
{
    set<int> st;
    st.insert(10);
    st.insert(20);
    st.insert(50);
    st.insert(40);
    st.insert(30);
    st.insert(30);
    
    st.erase(st.begin());
    printS(st); 

    st.erase(30);
    printS(st); 

    st.clear();
    printS(st); 
}

查找和统计

函数原型

  • find(key); 查找key是否存在,若存在,返回该元素的迭代器,反之返回set.end()
  • count(key);
void test04()
{
    set<int> st;
    st.insert(10);
    st.insert(20);
    st.insert(50);
    st.insert(40);
    st.insert(30);
    st.insert(30);
    
    set<int>::iterator pos = st.find(30);
    if (pos != st.end())
        cout << *pos << endl;

    int num = st.count(30);  // 对于set而言 不是1就是0
    cout << num << endl;
}

区别

  • set不可以插入重复的数据,即使插入也无效,multiset反之
  • set插入数据的同时会返回插入结果,表示是否插入成功
  • multiset不会检测数据,因此可以插入重复数据
void test05()
{
    set<int> st;
    pair<set<int>::iterator, bool> ret = st.insert(10);
    if (ret.second)
        cout << "OK!" << endl;

    ret = st.insert(10); 

    if (ret.second)
        cout << "OK!" << endl;
   
    multiset<int> mst;
    mst.insert(10);
    mst.insert(10);
    mst.insert(10);
    printS(mst);
}

pair对组创建

#pair对组

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

两种创建方式

  • pair<type, type> p(value1, value2);
  • pair<type, type> p = make_pair(value1, value2);
void test06()
{
    pair<string, int> p("Tom", 20);
    cout << "name: " << p.first << " age: " << p.second << endl;

    pair<string, int> p2 = make_pair("Jerry", 20);
    cout << "name: " << p2.first << " age: " << p2.second << endl;
}

排序

  • set 容器默认从小到大排序,可以利用仿函数改变排序规则 #仿函数

内置数据类型

class Compare
{
public:
    // 新标准下,必须加 const 以防止改变数据
    bool operator()(int val1, int val2) const
    {
        return val1 > val2;
    }
};

void test07()
{
    set<int, Compare> st;
    st.insert(10);
    st.insert(20);
    st.insert(50);
    st.insert(40);
    st.insert(30);
    st.insert(30);
    printSC(st);
}

自定义数据类型

class Person
{
public:
    Person() = default;
    Person(string name, int age) :
        m_Name(name), m_Age(age)
    {}

    string m_Name;
    int    m_Age;
};

class Compare2
{
public:
    bool operator()(const Person &p1, const Person &p2) const
    {
        return p1.m_Age > p2.m_Age;
    }
};
void printS(const set<Person, Compare2> &s1)
{
    for (set<Person, Compare2>::const_iterator it = s1.begin();
            it != s1.end(); it++)
    {
        cout << "name: " << it->m_Name << " age: " << it->m_Age << endl;
    }
}

void test08()
{
    // 自定义数据类型都会指定排序规则
    set<Person, Compare2> s1;
    Person p1("play1", 23);
    Person p2("play2", 22);
    Person p3("play3", 29);
    Person p4("play4", 21);
    Person p5("play5", 22);

    s1.insert(p1);
    s1.insert(p2);
    s1.insert(p3);
    s1.insert(p4);
    s1.insert(p5);
    printS(s1);
}

queue

  • **queue是一种先进先出(first in first out, FIFO)的数据结构,它有两个口
  • 队列允许从一端新增数据,从另一端移除数据
  • 队列只有对头和队尾可以被外界使用,不支持遍历

常用接口

  • 构造函数
    • queue<T> que;
    • queue(const queue &que);
  • 赋值操作
    • queue& operator=(const queue &que);
  • 数据存取
    • push(elem);
    • pop();
    • back();
    • front();
  • 大小操作
    • empty();
    • size();
void test01()
{
    queue<Person> que;
    Person p1("wuyan1", 1);
    Person p2("wuyan2", 2);
    Person p3("wuyan3", 3);
    que.push(p1);
    que.push(p2);
    que.push(p3);
    if (!que.empty())
        cout << "size: " << que.size() << endl;
    while(!que.empty())
    {
        cout << "front(): " << que.front().m_Name << " " <<         que.front().m_Age << endl;
        cout << "back(): " << que.back().m_Name << " " << que.back().m_Age << endl;
        que.pop();  // 从对头移除一个元素
    }
    cout << "size: " << que.size() << endl;
}

map multimap

  • map中所有元素都是pair
  • pair中的一个元素是键值(key),起到索引作用,第二个元素为实值(value)
  • 所有元素会根据键值自动排序

map/multimap属于关联式容器,底层结构是用二叉树实现的
#关联式容器
![[msedge_b6jI4jWOob.png]]

  • 只有map类型(unordered_map、unordered_multimap、multimap和map)才定义了mapped_type。
  • 当解引用一个关联容器迭代器时,我们会得到一个类型为容器的value_type的值的引用。对map而言,value_type是一个pair类型,其first成员保存const的关键字,second成员保存值
  • 一个map的value_type是一个pair,我们可以改变pair的值,但不能改变关键字成员的值
  • 对一个map使用下标操作,其行为与数组或vector上的下标操作很不相同:使用一个不在容器中的关键字作为下标,会添加一个具有此关键字的元素到map中。

优点

  • 可以根据 key 值快速找到value值
    区别
  • map不允许容器中有重复的key值元素
  • multimap 反之

构造和赋值

函数原型

构造

  • map<T1, T2> mp;
  • map(const map &mp);
    赋值
  • map& operator=(const map &mp);
void printM(map<int , int> &mp)
{
    for (const auto &m :mp)
        cout << "key: " << m.first << " value: " << m.second << endl;
    cout << endl;
}

void test01()
{
    map<int, int> m1;
    m1.insert(pair<int, int>(1, 10));
    m1.insert(pair<int, int>(3, 30));
    m1.insert(pair<int, int>(2, 20));
    printM(m1);

    map<int, int> m2(m1);
    printM(m2);

    map<int, int> m3;
    m3 = m2;
    printM(m3);
}

大小和交换

函数原型

  • size();
  • empty();
  • swap(mt);
void test02()
{
    map<int, int> m1;
    m1.insert(pair<int, int>(1, 10));
    m1.insert(pair<int, int>(3, 30));
    m1.insert(pair<int, int>(2, 20));
    printM(m1);

    if (!m1.empty())
        cout << "size: " << m1.size() << endl;


    map<int, int> m2;
    m2.insert(pair<int, int>(8, 100));
    m2.insert(pair<int, int>(7, 300));
    m2.insert(pair<int, int>(6, 200));
    m1.swap(m2);
    printM(m1);
    printM(m2);
}

插入和删除

函数原型

  • insert(elem);
  • clear();
  • erase(pos);
  • erase(beg. end);
  • erase(key);
- void test03()
{
    map<int, int> m1;
    m1.insert(pair<int, int>(1, 10));
    m1.insert(pair<int, int>(3, 30));
    m1.insert(pair<int, int>(2, 20));

    m1.insert(make_pair(4, 200));
    m1.insert(map<int, int>::value_type(5, 200));

    m1[7] = 100;  // 不建议用这种方式
    // 不建议插入,可以利用key访问value
    cout << m1[7] << endl;

    printM(m1);

    m1.erase(m1.begin());
    printM(m1);

    m1.erase(7);  //key
    printM(m1);

    m1.erase(m1.begin(), m1.end());
    m1.clear();
    printM(m1);
}

查找和统计

函数原型

  • find(key); 查找key是否存在,若存在返回该键元素的迭代器,否则返回set.end()
  • count(key); 统计key的元素个数
void test04()
{
    map<int, int> m1;
    m1.insert(pair<int, int>(1, 10));
    m1.insert(pair<int, int>(3, 30));
    m1.insert(pair<int, int>(2, 20));

    map<int, int>::iterator pos = m1.find(3);
    if ( pos != m1.end())
        cout << "key: " << pos->first << " value: " << pos->second << endl;

    cout << "count: " << m1.count(3) << endl;
}

排序

  • 利用仿函数
class Compare
{
public:
    bool operator()(int val1, int val2) const
    {
        return val1 > val2;
    }
};
void printM(map<int , int, Compare> &mp)
{
    for (const auto &m :mp)
        cout << "key: " << m.first << " value: " << m.second << endl;
    cout << endl;
}

void test05()
{
    map<int, int, Compare> m1;
    m1.insert(pair<int, int>(1, 10));
    m1.insert(pair<int, int>(3, 30));
    m1.insert(pair<int, int>(2, 20));
    printM(m1); 
}

案例

#include <iostream>
#include <vector>
#include <string>
#include <ctime>
#include <map>
using namespace std;

#define CHEHUA 0
#define MEISHU 1
#define YANFA  2

class Worker
{
public:

    string m_Name;
    int    m_Salary;
};

void creat_worker(vector<Worker> &v)
{
    string nameSeed = "ABCDEFGHIJ";
    for (int i = 0; i < 10; i++)
    {
        Worker wk;
        wk.m_Name = "员工 ";
        wk.m_Name += nameSeed[i];

        wk.m_Salary = rand() % 10000 + 10000;
        v.push_back(wk);
    }
}

void printV(vector<Worker> &vec)
{
    for (const auto &v:vec)
        cout << "姓名: " << v.m_Name << "  工资: " << v.m_Salary << endl;
}

void set_group(const vector<Worker> &vec, multimap<int, Worker> &m)
{
    for (const auto &v:vec)
    {
        int depId = rand() % 3; // 0 1 2
        m.insert(make_pair(depId, v));
    }
}

void showWorkerByGroup(multimap<int, Worker> &m)
{
    cout << "-----策划部门-----" << endl;
    multimap<int, Worker>::const_iterator pos = m.find(CHEHUA);
    int count = m.count(CHEHUA);
    int index = 0;
    for (; pos != m.end() && index < count; pos++, index++)
        cout << "姓名: " << pos->second.m_Name << \
            "  工资: " << pos->second.m_Salary << endl;
    cout << "-----美术部门-----" << endl;
    pos = m.find(MEISHU);
    count = m.count(MEISHU);
    index = 0;
    for (; pos != m.end() && index < count; pos++, index++)
        cout << "姓名: " << pos->second.m_Name << \
            "  工资: " << pos->second.m_Salary << endl;
    cout << "-----研发部门-----" << endl;
    pos = m.find(YANFA);
    count = m.count(YANFA);
    index = 0;
    for (; pos != m.end() && index < count; pos++, index++)
        cout << "姓名: " << pos->second.m_Name << \
            "  工资: " << pos->second.m_Salary << endl;
}

void test01()
{
    srand((unsigned int) time(NULL));
    // 1
    vector<Worker> vw;
    creat_worker(vw);
    // 2
    multimap<int, Worker> mw;
    set_group(vw, mw);
    // 3
    showWorkerByGroup(mw);
}

int main()
{
    test01();
    return 0;
}

list

  • 链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
  • 链表的组成:链表是由一系列节点组成的
  • 节点的组成:一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针域
  • STL中的链表是一个双向循环链表
    ![[list.jpg]]
  • **链表的存储不是连续的内存空间,因此链表中的迭代器只支持前移和后移,属于双向迭代器
  • 优点:可以相对快速的在任一位置进行插入和删除元素,采用动态内存分配,不会造成内存浪费和溢出
  • 缺点:遍历速度较慢,占用空间较大
    list有一个重要的性质插入和删除操作都不会造成list迭代器的失效,这在vector中是不成立的

构造函数

函数原型

  • list<T> lst;
  • list(beg, end);
  • list(n, elem);
  • list(const list &lst);
void test01()
{
    list<int> l1;
    for (int i = 0; i < 5; i++)
        l1.push_back(i);

    list<int> l2(l1.begin(), l1.end());

    list<int> l3(l2);

    list<int> l4(10, 1000);
}

赋值和交换

函数原型

  • assign(beg, end);
  • assign(n, elem);
  • list& operator=(const list &lst);
  • swap(lst);
void test02()
{
    list<int> l1;
    for (int i = 0; i < 5; i++)
        l1.push_back(i);
    
    list<int> l2;
    l2 = l1;

    list<int> l3;
    l3.assign(l2.begin(), l2.end());

    list<int> l4;
    l4.assign(5, 888);

    l1.swap(l4);
}

大小操作

函数原型

  • size();
  • empty();
  • resize(num);
  • resize(num, elem);
void test03()
{
    list<int> l1;
    for (int i = 0; i < 5; i++)
        l1.push_back(i);
    if (!l1.empty())
        cout << "size: " << l1.size() << endl;
    l1.resize(2);

    l1.resize(6, 10);
}

插入和删除

函数原型

  • push_back(elem);
  • pop_back();
  • push_front(elem);
  • pop_front();
  • insert(pos, elem);
  • insert(pos, n, elem);
  • insert(pos, beg, end);
  • clear();
  • erase(beg, end);
  • erase(pos);
  • remove(elem); 删除容器中所有与elem匹配的元素
void test04()
{
    list<int> l1;
    for (int i = 0; i < 5; i++)
        l1.push_back(i);
    for (int i = 10; i < 15; i++)
        l1.push_front(i);
    
    l1.pop_back(); //尾删

    l1.pop_front(); //头删

    // l1.insert(l1.begin() + 1, 888); err 不支持随机访问
    // list<int>::iterator it = l1.begin();
    auto it = l1.begin();
    l1.insert(++it, 888);
    // 如果既支持 ++ 又支持 -- 说明是双向循环

    l1.erase(l1.begin());

    l1.remove(2);  //删除所有和2匹配的元素

    l1.clear();
}
  • tips 如果既支持 ++ 又支持 - - 说明是双向循环,迭代器不可以 + 1的话,说明不支持随机访问
    #随机访问迭代器

数据存取

函数原型

  • front();
  • back();
void test05()
{
    list<int> l1;
    for (int i = 0; i < 5; i++)
        l1.push_back(i);
    cout << "l1.front(): " << l1.front() << endl;
    cout << "l1.back(): " << l1.back() << endl;
}

反转和排序

函数原型

  • reverse();
  • sort();
    所有不支持随机访问迭代器的容器,不可以使用标准算法,它自己内部会提供一些算法
    #随机访问迭代器
bool m_compare(int val1, int val2)
{
    // 降序 就让第一个数大于第二个数
    return val1 > val2;
}

void test06()
{
    list<int> l1;
    for (int i = 0; i < 5; i++)
        l1.push_back(i);
    l1.reverse();  // 反转相当于倒序 不是排序
    printL(l1);

    list<int> l2;
    l2.push_back(3);
    l2.push_back(2);
    l2.push_back(0);
    l2.push_back(9);
    l2.push_back(1);
    
    // sort(l2.begin(), l2.end()); err
    l2.sort(m_compare);  // 默认从小到大
    printL(l2);
}


deque

  • 双端数组,可以对头端进行插入删除操作

deque和vector的区别

  • vector对于头部的插入删除效率低,数据量越大,效率越低
  • deque相对于而言,对头部的插入删除速度比vector快
  • vector访问元素时的速度会比deque快,这和两者的内部实现有关
  • deque容器没有容量概念,因为它可以无限放数据
    ![[deque.jpg]]

内部实现原理

deque 内部有个中控器, 维护每段缓冲区中的内容,缓冲区存放真实数据
中控器维护每个缓冲区的地址,使得使用deque时像一片连续的内存空间
![[deque_中控器.jpg]]

  • deque 的迭代器支持随机访问

构造函数

函数原型

  • deque<int> deq;
  • deque(beg, end);
  • deque(n, elem);
  • deque(const deque &deq);
void test01()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
        d1.push_back(i);

    deque<int> d2(d1.begin(), d1.end());

    deque<int> d3(10, 100);

    deque<int> d4(d3);
}

赋值操作

函数原型

  • deque& operator=(const deque &deq);
  • assign(beg, end);
  • assign(n, elem);
void test02()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
        d1.push_back(i);

    // operator=
    deque<int> d2;
    d2 = d1;

    deque<int> d3;
    d3.assign(d1.begin(), d1.end());

    deque<int> d4;
    d4.assign(10, 100);
}

大小操作

函数原型

  • deque.empty();
  • deque.size();
  • deque.resize(num);
  • deque.resize(num, elem);
void test03()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
        d1.push_back(i);
    if (!d1.empty())
        // deque容器没有容量概念,因为它可以无限放数据
        cout << "size: " << d1.size() << endl;
    // resize
    // d1.resize(15);
    printD(d1);

    d1.resize(15, 1);

    d1.resize(5);
}

插入和删除

函数原型

  • 两端插入操作
    • push_back(elem);
    • push_front(elem);
    • pop_back();
    • pop_front();
  • 指定位置操作
    • insert(pos, elem);
    • insert(pos, n, elem);
    • insert(pos, beg, end);
    • clear();
    • erase(beg, end);
    • erase(pos);
void test04()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
        d1.push_front(i);
    for (int i = 1; i < 10; i++)
        d1.push_back(i);

    d1.pop_back();

    d1.pop_front();

    d1.insert(d1.begin(), 1000);

    d1.insert(d1.begin(), 2, 10000);

    // 区间插入
    deque<int> d2;
    d2.push_back(1);
    d2.push_back(2);
    d2.push_back(3);
    // 在d1 begin 位置 插入d2的begin 到 end 位置
    d1.insert(d1.begin(), d2.begin(), d2.end());
}
// 删除
void test05()
{
    deque<int> d1;
    d1.push_back(10);
    d1.push_back(20);
    d1.push_back(30);
    d1.push_back(40);

    auto it = d1.begin();
    it++;
    d1.erase(it);

    d1.erase(d1.begin(), d1.end());
    d1.clear();
}

数据存取

函数原型

  • at(int idx);
  • operator[];
  • front();
  • back();
void test06()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
        d1.push_front(i);
    for (int i = 0; i < d1.size(); i++)
        cout << d1[i] << " ";
    cout << endl;
    for (int i = 0; i < d1.size(); i++)
        cout << d1.at(i) << " ";
    cout << endl;
    cout << "front(): " << d1.front() << endl;
    cout << "back(): " << d1.back() << endl;
}

排序

对于支持随机访问迭代器的容器,都可以利用sort算法排序

  • 算法
    • sort(iterator beg, iterator end) 对begin和end区间内元素进行排序
void test07()
{
    deque<int> d1;
    d1.push_back(10);
    d1.push_back(20);
    d1.push_front(100);
    d1.push_front(1000);
    printD(d1);
    sort(d1.begin(), d1.end());
    printD(d1);
}

案例

  • 随机数 #随机数
    • rand() % num + num
  • 随机数种子
    • 需要头文件<ctime>
    • srand((unsigned int) time(NULL));
#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <ctime>
#include <algorithm>
using namespace std;

class Person
{
public:
    Person() = default;
    Person(string name, int score) :
        m_Name(name), m_Score(score)
    {
    }

    string m_Name;
    int    m_Score;
};

void creatPerson(vector<Person> &v)
{
    string nameSeed = "ABCDE";
    for (int i = 0; i < 5; i++)
    {
        string name = "Player";
        name += nameSeed[i];
        int score = 0;
        Person p(name, score);
        v.push_back(p);
    }
}

void printPerson(const vector<Person> &vec)
{
    for (const auto &v : vec)
        cout << "name: " << v.m_Name 
            << " score: " << v.m_Score << endl;
}

// 打分
void setScore(vector<Person> &vec)
{
    for (vector<Person>::iterator it = vec.begin(); it != vec.end(); it++)
    {
        deque<int> _sco;
        // 十个人打分
        for (int i = 0; i < 10; i++)
        {
            int score = rand() % 41 + 60; // 随机数 60 ~ 100
            _sco.push_back(score);
        }
        //cout << "name: " << it->m_Name << " score: " << endl;
        //for (auto &d : _sco)
        //    cout << d << endl;
        // 排序
        sort(_sco.begin(), _sco.end());
        // 去除最高分和最低分
        // 通过排序后,进行尾删和头删。相当于去除最高分和最低分
        _sco.pop_back();
        _sco.pop_front();
        // 取平均分
        int sum = 0;
        // c++11 范围for
        for (auto &d : _sco)
            sum += d;
        int avg = sum / _sco.size();
        // 平均分赋值给选手
        it->m_Score = avg;
    }
}

void test01()
{
	// 随机数种子
	srand((unsigned int) time(NULL));
    vector<Person> vec;
    creatPerson(vec);
    printPerson(vec);
    setScore(vec);
    printPerson(vec);
}


int main()
{
    test01();
    return 0;
}

无序容器

新标准定义了4个无序关联容器(unordered associative container)。这些容器不是使用比较运算符来组织元素,而是使用一个**哈希函数**(hash function)和关键字类型的`==`运算符。在关键字类型的元素没有明显的序关系的情况下,无序容器是非常有用的。在某些应用中,维护元素的序代价非常高昂,此时无序容器也很有用。
  • 除了hash管理操作外,无序容器依然有与有序容器相同的操作(find,insert)等

  • 无序容器也有允许重复关键字的版本

  • 通常可以用一个无序容器替换对应的有序容器,反之亦然。但是,由于元素未按顺序存储,一个使用无序容器的程序的输出(通常)会与使用有序容器的版本不同。
    ![[msedge_1QSiibx2od.png]]

  • unordered_map 保存关键字-值对的容器,不允许重复关键字。

  • unordered_multimap 保存关键字-值对的容器,允许重复关键字。

  • unordered_multiset 保存关键字的容器,允许重复关键字。

  • unordered_set 保存关键字的容器,不允许重复关键字


常用算法

  • 算法重要是由头文件<algorithm> <functional> <numeric>组成
  • <algorithm> 是所有STL头文件中最大的一个,范围涉及到:比较,交换,查找,遍历操作,复制,修改等等
  • <numeric>体积很小,只包括几个在序列上面进行简单数学运算的模版函数
  • <functional>定义了一些类模版,用以声明函数对象

常用遍历算法

  • for_each 遍历容器
  • transform 搬运容器到另一个容器中

for_each

void print01(int val)
{
    cout << val << " ";
}

class Print02
{
public:
    void operator()(int val)
    {
        cout << val << endl;
    }
};

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);

    for_each(v1.begin(), v1.end(), print01);
    cout << endl;
    for_each(v1.begin(), v1.end(), Print02());
}

transform

  • transform(iterator beg1, iterator end1, iterator beg2, _func);
class Transform
{
public:
    int operator()(int v)
    {
        return v + 100;
    }
};
class Print02
{
public:
    void operator()(int val)
    {
        cout << val << " ";
    }
};

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);

    vector<int> vTarget;
    vTarget.resize(v1.size()); // 必须提前指定空间
    transform(v1.begin(), v1.end(), vTarget.begin(), Transform());
    for_each(vTarget.begin(), vTarget.end(), Print02());
}

常用查找算法

  • find() -查找元素
  • find_if() -按条件查找元素
  • adjacent_find() -查找相邻重复元素
  • binary_search() -二分查找
  • count() -统计元素个数
  • count_if() -按条件统计元素个数

find

查找指定元素,找到返回指定元素的迭代器,否则返回end()

  • find(iterator beg, iterator end, value);
void test01()
{
    vector<int> v;
    for (int i = 0; i < 10; i++)
        v.push_back(i);

    auto pos = find(v.begin(), v.end(), 5);
    if (pos != v.end())
        cout << *pos << endl;
}

class Person
{
public:
    Person() = default;
    Person(string name, int age) :
        m_Name(name), m_Age(age)
    {}
    //需要重载==
    bool operator==(const Person &p) const
    {
        if (m_Name == p.m_Name && m_Age == p.m_Age)
            return true;
        return false;
    }

    string m_Name;
    int m_Age;
};

void test02()
{
    vector<Person> vp;
    Person p1("play1", 10);
    Person p2("play2", 20);
    Person p3("play3", 30);
    Person p4("play4", 40);
    vp.push_back(p1);
    vp.push_back(p2);
    vp.push_back(p3);
    vp.push_back(p4);

    Person ptem("play2", 20);
    auto pos = find(vp.begin(), vp.end(), ptem);
    if (pos != vp.end())
        cout << pos->m_Name << " " << pos->m_Age << endl;
}

find_if

  • find_if(iterator beg, iterator end, _Pred); _Pred 函数或者谓词
class GreaterFive
{
public:
    bool operator()(int val)
    {
        return val > 5;
    }
};

void test01()
{
    vector<int> v;
    for (int i = 0; i < 10; i++)
        v.push_back(i);
    
    auto pos = find_if(v.begin(), v.end(), GreaterFive());
    if (pos != v.end())
        cout << *pos << endl;
}

class Person
{
public:
    Person() = default;
    Person(string name, int age) :
        m_Name(name), m_Age(age)
    {}

    string m_Name;
    int m_Age;
};

class Greater20
{
public:
    bool operator()(const Person &p)
    {
        return p.m_Age > 20;
    }
};

void test02()
{
    vector<Person> vp;
    Person p1("play1", 10);
    Person p2("play2", 20);
    Person p3("play3", 30);
    Person p4("play4", 40);
    vp.push_back(p1);
    vp.push_back(p2);
    vp.push_back(p3);
    vp.push_back(p4);

    auto pos = find_if(vp.begin(), vp.end(), Greater20());
    if (pos != vp.end())
        cout << pos->m_Name << " " << pos->m_Age << endl;
}

adjacent_find

  • 查找相邻重复元素
    • adjacent_find(iterator beg, iterator end);
void test01()
{
    vector<int> v;
    v.push_back(0);
    v.push_back(2);
    v.push_back(0);
    v.push_back(3);
    v.push_back(1);
    v.push_back(3);
    v.push_back(3);
    
    auto pos = adjacent_find(v.begin(), v.end());
    if (pos != v.end())
        cout << *pos << endl;
}

binary_search

注意:在无序序列中不可以使用

  • 二分查找 返回true 或 false
    • bool binary_search(iterator beg, iterator end, value);
void test01()
{
    vector<int> v;
    for (int i = 0; i < 10; i++)
        v.push_back(i);

    if (binary_search(v.begin(), v.end(), 9))
        cout << "OK!" << endl;
}


count

统计元素出现个数

  • count(iterator beg, iterator end, value);
void test01()
{
    vector<int> v;
    v.push_back(10);
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(50);
    v.push_back(50);

    cout << count(v.begin(), v.end(), 10) << endl;
}

class Person
{
public:
    Person() = default;
    Person(string name, int age) :
        m_Name(name), m_Age(age)
    {}
    bool operator==(const Person &p) const
    {
        if (m_Age == p.m_Age)
            return true;
        return false;
    }

    string m_Name;
    int m_Age;
};

void test02()
{
    vector<Person> vp;
    Person p1("play1", 10);
    Person p2("play2", 20);
    Person p3("play3", 20);
    Person p4("play4", 30);
    vp.push_back(p1);
    vp.push_back(p2);
    vp.push_back(p3);
    vp.push_back(p4);
    Person p5("play5", 20);
    cout << count(vp.begin(), vp.end(), p5) << endl;
}

count_if

  • 按条件统计元素个数
    • count_if(iterator beg, iterator end, _Pred);
class Creater20
{
public:
    bool operator()(int val) const
    {
        return val > 20;
    }
};

void test01()
{
    vector<int> v;
    v.push_back(10);
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(50);
    v.push_back(50);

    int num = count_if(v.begin(), v.end(), Creater20());
    cout << num << endl;
}

class Person
{
public:
    Person() = default;
    Person(string name, int age) :
        m_Name(name), m_Age(age)
    {}

    string m_Name;
    int m_Age;
};

class CreaterPerson
{
public:
    bool operator()(const Person &p) const
    {
        return p.m_Age > 10;
    }
};

void test02()
{
    vector<Person> vp;
    Person p1("play1", 10);
    Person p2("play2", 20);
    Person p3("play3", 20);
    Person p4("play4", 30);
    vp.push_back(p1);
    vp.push_back(p2);
    vp.push_back(p3);
    vp.push_back(p4);
    cout << count_if(vp.begin(), vp.end(), CreaterPerson()) << endl;
}

常用排序算法

  • sort
  • random_shuffle -洗牌 指定范围内的元素随机调整次序
  • merge -容器元素合并,并存储到另一容器中
  • reverse - 反转指定范围的元素

sort

  • sort(iterator beg, iterator end, _Pred);
void printV(int val)
{
    cout << val << " ";
}

class SortV
{
public:
    bool operator()(int val1, int val2) const
    {
        return val1 > val2; 
    }
};

void test01()
{
    vector<int> v1;
    v1.push_back(10);
    v1.push_back(30);
    v1.push_back(20);
    v1.push_back(90);
    v1.push_back(60);

    sort(v1.begin(), v1.end());
    for_each(v1.begin(), v1.end(), printV);
    cout << endl;
    //sort(v1.begin(), v1.end(), SortV());
    sort(v1.begin(), v1.end(), greater<int>());
    for_each(v1.begin(), v1.end(), printV);

}

random_shuffle

  • 洗牌 将指定范围内的元素随机调整次序
  • random_shuffle(iterator beg, iterator end);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
	srand((unsignde int) time(NULL));
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);

    // 打乱顺序
    random_shuffle(v1.begin(), v1.end());
    for_each(v1.begin(), v1.end(), printV);
}

merge

两个容器元素合并,并存储到另一个容器中 两个容器必须是有序的

  • merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);
    vector<int> v2;
    for (int i = 5; i < 15; i++)
        v1.push_back(i);

    // target
    vector<int> vTarget;

    vTarget.resize(v1.size() + v2.size());

    merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
    for_each(vTarget.begin(), vTarget.end(), printV);
}

reverse

将容器内的元素进行反转

  • reverse(iterator beg, iterator end);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);

    for_each(v1.begin(), v1.end(), printV);

    reverse(v1.begin(), v1.end());
    for_each(v1.begin(), v1.end(), printV);
}

常用拷贝和替换算法

  • copy
  • replace - 将容器内指定范围的旧元素修改为新元素
  • replace_if - 同上 按照条件
  • swap

copy

  • copy(iterator beg1, iterator end1, iterator beg2);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);
    vector<int> v2;
    v2.resize(v1.size());

    copy(v1.begin(), v1.end(), v2.begin());
    for_each(v2.begin(), v2.end(), printV);
}

replace

将指定容器内的旧元素替换为新元素

  • replace(iterator beg, iterator end, oldvalue, newvalue);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);
    v1.push_back(5); 

    replace(v1.begin(), v1.end(), 5, 888);
    for_each(v1.begin(), v1.end(), printV);
}

replace_if

将区间内满足条件的元素,替换为新元素

  • replace_if(iterator beg, iterator end, _Pred, newvalue);
void printV(int val)
{
    cout << val << " ";
}

class ReplaceV
{
public:
    bool operator()(int val) const
    {
        return val > 0;
    }
};

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);
    v1.push_back(5); 

    replace_if(v1.begin(), v1.end(), ReplaceV(), 888);
    for_each(v1.begin(), v1.end(), printV);
}

swap

  • swap(container c1, container c2);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);

    v1.push_back(10);  // OK!
    vector<int> v2;
    for (int i = 5; i < 15; i++)
        v2.push_back(i);

    swap(v1, v2);

    for_each(v1.begin(), v1.end(), printV);
    cout << endl;
    for_each(v2.begin(), v2.end(), printV);
}

常用算术生成算法

  • 包含头文件为<numeric>

  • accumulate - 计算容器元素累计总和

  • fill - 向容器中添加元素


accumulate

计算容器元素累计总和

  • accumulate(iterator beg, iterator end, value);
void test01()
{
    vector<int> v1;
    for (int i = 0; i <= 10000; i++)
        v1.push_back(i);

    // 参数3 是一个起始的累加值
    int total = accumulate(v1.begin(), v1.end(), 10);
    cout << total << endl;

}

fill

向容器中填充指定的元素

  • fill(iterator beg, iterator end, value);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
    vector<int> v1;
    v1.resize(10);

    // 后期填充
    fill(v1.begin(), v1.end(), 88); 
    for_each(v1.begin(), v1.end(), printV);
}

常用集合算法

  • set_intersection - 求两个容器的交集 两个集合重复的元素
  • set_union - 求两个容器的并集 两个集合不重复的元素
  • set_difference - 求两个容器的差集
    • 差集 如果是求 v1 和 v2 的差集:那么就是v1里面不是交集的一部分
    • v2 和 v1 则反之

set_intersection

求交集

  • set_intersection(iterator b1, iterator e1, iterator b2, iterator e2, iterator dest);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
    vector<int> v1;
    vector<int> v2;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);  // 0 ` 9
        v2.push_back(i + 5); // 5 ` 14
    }
    vector<int> vTarget;
    //最特殊的情况 大容器包含小容器 ,开辟空间 取小容器
    vTarget.resize(min(v1.size(), v2.size()));
    // 返回end迭代器
    vector<int>::iterator posEnd = set_intersection(v1.begin(), v1.end(),\
        v2.begin(), v2.end(), vTarget.begin());
    // for_each(vTarget.begin(), vTarget.end(), printV);
    for_each(vTarget.begin(), posEnd, printV);
}

set_union

求两个集合的并集 两个集合必须是有序序列

  • set_union(同上);
void printV(int val)
{
    cout << val << " ";
}

void test01()
{
    vector<int> v1;
    vector<int> v2;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);  // 0 ` 9
        v2.push_back(i + 5); // 5 ` 14
    }
    vector<int> vTarget;
    //最特殊的情况 两个容器没有并集
    vTarget.resize(v1.size() +  v2.size());
    // 返回end迭代器
    vector<int>::iterator posEnd = set_union(v1.begin(), v1.end(),\
        v2.begin(), v2.end(), vTarget.begin());
    // for_each(vTarget.begin(), vTarget.end(), printV);
    for_each(vTarget.begin(), posEnd, printV);
}

set_difference

求两个集合的差集, 两个集合必须是有序序列

  • set_defference(iterator beg1, iterator beg2, …仿上);
void test01()
{
    vector<int> v1;
    vector<int> v2;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);  // 0 ` 9
        v2.push_back(i + 5); // 5 ` 14
    }
    vector<int> vTarget;
    // 最特殊的情况 两个容器没有交集,取两个容器中大的
    vTarget.resize(max(v1.size(), v2.size()));
    // 返回end迭代器
    vector<int>::iterator posEnd = set_difference(v1.begin(), v1.end(),\
        v2.begin(), v2.end(), vTarget.begin());
    // for_each(vTarget.begin(), vTarget.end(), printV);
    for_each(vTarget.begin(), posEnd, printV);
    cout << endl;
    // v2 v1
    posEnd = set_difference(v2.begin(), v2.end(),\
        v1.begin(), v1.end(), vTarget.begin());
    // for_each(vTarget.begin(), vTarget.end(), printV);
    for_each(vTarget.begin(), posEnd, printV);
}

迭代器相关

除了为每个容器定义的迭代器之外,标准库在头文件iterator中还定义了额外几种迭代器。这些迭代器包括以下几种。

  • 插入迭代器(insert iterator):这些迭代器被绑定到一个容器上,可用来向容器插入元素
  • 流迭代器(stream iterator):这些迭代器被绑定到输入或输出流上,可用来遍历所关联的IO流。
  • 反向迭代器(reverse iterator):这些迭代器向后而不是向前移动。除了forward_list之外的标准库容器都有反向迭代器。
  • 移动迭代器(move iterator):这些专用的迭代器不是拷贝其中的元素,而是移动它们。

流迭代器

// istream_iterator
void test01()
{
    vector<int> vec;
    istream_iterator<int> in(cin);  // 指定流
    istream_iterator<int> int_eof; // 默认构造为尾迭代器
    while (in != int_eof)
        vec.push_back(*in++);
}

void test02()
{
    istream_iterator<int> in(cin), eof;
    cout << accumulate(in, eof, 0) << endl;
}

反向迭代器相关问题

![[Pasted image 20231103195629.png]]

  • 反向迭代器,在使用时会反向处理,达不到预期效果(详见代码), 可以使用成员函数base将其转换回普通迭代器
    ![[Pasted image 20231103195708.png]]
  • 反向迭代器的目的是表示元素范围,而这些范围是不对称的,这导致一个重要的结果:当我们从一个普通迭代器初始化一个反向迭代器,或是给一个反向迭代器赋值时,结果迭代器与原迭代器指向的并不是相同的元素。
// 反向迭代器
void test03()
{
    string str = "hello, world, wuyan";
    auto pos = find(str.cbegin(), str.cend(), ',');
    cout << string(str.cbegin(), pos) << endl;   // helloc
    auto rpos = find(str.crbegin(), str.crend(), ',');
    cout << string(str.crbegin(), rpos) << endl; // nayuw
    // 将reverse_iterator 转换为一个普通迭代器,得到一个正向迭代器
    cout << string(rpos.base(), str.cend()) << endl; // wuyan
}

五种迭代器类别

  • 输入迭代器:只读,不写,单遍扫描,只能递增
  • 输出迭代器:只写,不读,单遍扫描,只能递增
  • 前向迭代器:可读写,多遍扫描,只能递增
  • 双向迭代器:可读写,多遍扫描,可递增递减
  • 随机访问迭代器:可读写,多遍扫描,支持全部迭代器运算
    #五种迭代器类别
  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值