【C++基础】09:STL(一)

news2024/11/15 18:21:25

STL(一)


OVERVIEW

  • STL(一)
      • 一、STL初识
        • 1.STL六大组件:
        • 2.容器&迭代器:
      • 二、STLGroup1
        • 1.string容器:
          • (1)基本概念:
          • (2)string构造函数:
          • (3)string赋值操作:
          • (4)string常用操作:
            • <1>字符串拼接:
            • <2>更多操作:
        • 2.vector容器:
          • (1)vector单端数组:
          • (2)vector构造&赋值:
          • (3)vector容量与大小:
          • (4)vector常用操作:
            • <1>插入与删除:
            • <2>数据访问/存取:
            • <3>容器互换:
            • <4>预留空间:
        • 3.deque容器:
          • (1)deque双端数组:
          • (2)deque构造&赋值:
          • (3)deque大小操作:
          • (4)deque常用操作:
            • <1>插入与删除:
            • <2>数据访问/存取:
            • <3>deque排序:
        • 4.综合运用1:评委打分
      • 三、STLGroup2
        • 1.stack容器:
          • (1)stack栈(FILO):
          • (2)stack常用接口:
        • 2.queue容器:
          • (1)queue队列(FIFO):
          • (2)queue常用接口:
        • 3.list容器:
          • (1)list链表:
          • (2)list构造&赋值:
          • (3)list大小操作:
          • (4)list常用操作:
            • <1>插入和删除:
            • <2>数据存取:
            • <3>反转和排序:
            • <4>list排序案例:
        • 4.set/multiset容器:
          • (1)set概念:
          • (2)set构造与赋值:
          • (3)set大小与交换:
          • (4)set常用操作:
            • <1>插入与删除:
            • <2>查找与统计:
            • <3>排序:
            • <4>pair对组创建:
        • 5.map/multimap容器:
          • (1)map概念:
          • (2)map构造与赋值:
          • (3)map大小与交换:
          • (4)map常用操作:
            • <1>插入与删除:
            • <2>查找与统计:
            • <3>排序:
        • 6.综合运用2:员工分组

一、STL初识

C++的面向对象和泛型编程(模板化)思想,其目的就是提高程序的复用性。

大多数情况下数据结构和算法都没有一套标准,导致被迫从事大量重复的工作,而STL就是这样一套标准。

STL广义上分为容器(container)、算法(algorithm)和迭代器(iterator),几乎其中所有的代码都采用了模板类or模板函数。

1.STL六大组件:

STL6个组件分别为容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

STL组件说明
容器各种数据结构用于存放数据,如vector、list、deque、set、map等
算法各种常用的算法,如sort、find、copy、for_each等
迭代器扮演了容器与算法之间的胶合剂
适配器一种用来修饰容器or仿函数or迭代器接口的东西
空间配置器负责空间的配置与管理

2.容器&迭代器:

容器分为序列式容器(强调值的排序每个元素均有固定的位置)与关联式容器(二叉树结构没有严格的物理顺序关系)。

迭代器为容器与算法之间的粘合剂,提供一种方法能够依序访问某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。

迭代器种类:

种类功能支持运算
输入迭代器对数据只读访问只读、支持++、==、!=
输出迭代器对数据只写访问只写、支持++
前向迭代器读写操作,并能向前推进迭代器读写、支持++、==、!=
双向迭代器★读写操作,并能向前和向后操作读写、支持++、–
随机访问迭代器★读写操作,可以以跳跃的方式访问任意数据(功能最强)读写、支持++、–、[n]、-n、<、>、<=、>=

注意:

  1. 每个容器都有自己的专属迭代器,其使用非常类型与指针(可以先理解为指针)
  2. 常用的容器中迭代器种类为双向迭代器、和随机访问迭代器

二、STLGroup1

1.string容器:

(1)基本概念:

string是C++风格的字符串,其本质上是一个类。

string与char*区别&联系:

  1. char*是一个指针
  2. string是一个内部封装了char*的类(char*容器),而内部的char*则用于维护这个字符串。
  3. string管理char*所分配的内存,不用担心赋值越界和取值越界等问题(由类的内部负责)。
(2)string构造函数:
构造函数说明
string();创建一个空字符串,如string str;
string(const char *s);使用字符串s初始化
string(const string &str);使用string对象初始化一个string对象
string(int n, char c);使用n个字符c初始化
(3)string赋值操作:

c++中进行字符串赋值,可以利用+=号操作,或者利用append()函数进行字符串赋值。

赋值函数说明
1.string& operator=(const char *s);将char*类型字符串,赋值给当前的字符串
2.string& operator=(const string &s);将string类型字符串s,赋值给当前的字符串
3.string& operator=(char c);将char类型字符c,赋值给当前字符串
4.string& assign(const char *s);把字符串s,赋给当前字符串
5.string& assign(const char *s, int n);把字符串s的前n个字符,赋给当前字符串
6.string& assign(const string &s);把字符串s,赋给当前字符串
7.string& assign(int n, char c);把n个字符c,赋给当前字符串
#include<iostream>
#include<string>
using namespace std;

void test01(){
    //1.string& operator=(const char *s);
    string str1;
    str1 = "hellow orld";
    cout << "str1 = " << str1 << endl;
    //2.string& operator=(const string &s);
    string str2;
    str2 = str1;
    cout << "str2 = " << str2 << endl;
    //3.string& operator=(char c);
    string str3;
    str3 = 'a';
    cout << "str3 = " << str3 << endl;
    //4.string& assign(const char *s);
    string str4;
    str4.assign("hello cpp");
    cout << "str4 = " << str4 << endl;
    //5.string& assign(const char *s, int n);
    string str5;
    str5.assign("hello cpp", 5);
    cout << "str5 = " << str5 << endl;
    //6.string& assign(const string &s);
    string str6;
    str6.assign(str5);
    cout << "str6 = " << str6 << endl;
    //7.string& assign(int n, char c);
    string str7;
    str7.assign(5, 'c');
    cout << "str7 = " << str7 << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220320133421813

(4)string常用操作:
<1>字符串拼接:

c++中实现string字符串拼接操作的有以下几种函数:

拼接函数说明
string& operator+=(const char* str);重载+=操作符
string& operator+=(const char c);重载+=操作符
string& operator+=(const string &str);重载+=操作符
string& append(const char *s);把字符串s连接到当前字符串结尾
string& append(const char *s, int n);把字符串s的前n个字符,连接到当前字符串结尾
string& append(const string &str);
string& append(const string &str, int pos, int n);把字符串str的pos位置后n个字符,连接到当前字符串结尾
<2>更多操作:
字符串操作cpp函数
插入和删除insert()、erase()
查找与替换find()、rfind()、replace()
字符串比较int compare(const string &str) const;
字符串访问(存取)char& operator[](int n);char& at(int n);
取子串string substr(int pos = 0. int n = npos) const;

注意:

  1. find()函数从下标0开始查找,如果没有目标则返回-1
  2. compare()函数按照ASCII码值进行对比,若相等返回0,大于0则第一个str值更大,小于0则第一个str值更小
  3. insert、erase与substr操作都是从下标0开始的

使用案例:

#include<iostream>
using namespace std;

void test01(){
    string email = "zhangsan@sina.com";
    int pos = email.find("@");
    string userName = email.substr(0, pos);
    cout << "userName:" << userName << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220321002408697

2.vector容器:

(1)vector单端数组:

vector数据结构与数组非常类似(也称单端数组),与String的不同之处在于vector容器可动态扩展空间大小

image-20220321075546606

注意:

  1. 动态扩展并不是在原来空间之后续接新的空间,而是找更大的内存空间(将原数据拷贝到新的空间,释放原空间)
  2. vector容器的迭代器是支持随机访问的迭代器
(2)vector构造&赋值:
构造函数说明
vector<T> v;采用模板实现类,默认构造函数
vector(v.begin(), v.end());将v[begin(), end())区间中的元素拷贝
vector(n, elem);将n个elem拷贝给本身
vector(const vector &vec);拷贝构造函数
赋值函数说明
vector& operator=(const vector &vec);重载等号操作符
assign(beg, end);区间赋值
assign(n, elem);n个elem赋值
#include<iostream>
#include<vector>
using namespace std;

void printVector(vector<int> &v){
    for(vector<int>::iterator it = v.begin(); it != v.end(); ++it){
        cout << *it << " ";
    }
    cout << endl;
}

void test01(){
    //1.默认无参构造
    vector<int> v1;
    for(int i = 0; i < 10; ++i){
        v1.push_back(i);
    }
    printVector(v1);
    //2.通过区间的方式构造
    vector<int> v2(v1.begin(), v1.end());
    printVector(v2);
    //3.n个elem方式构造
    vector<int> v3(10, 5);
    printVector(v3);
    //4.拷贝构造
    vector<int> v4(v3);
    printVector(v4);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220321081527816

(3)vector容量与大小:

对vector容器的容量和大小进行操作:

函数原型说明
empty()vector是否为空
size()vector中元素的个数
capacity()vector的容量
resize(int num);重新指定容器的长度(以默认值填充新位置)
resize(int num, elem);重新指定容器的长度(以elem值填充新位置)
(4)vector常用操作:
<1>插入与删除:
插入与删除说明
push_back(elem);尾部插入元素elem
pop_back();尾部删除一个元素
insert(const_iterator pos, elem);迭代器指向pos位置,插入元素elem
insert(const_iterator pos, int n, elem);迭代器指向pos位置,插入n个elem元素
erase(const_iterator pos);删除迭代器指向元素
erase(const_iterator start, const_iterator end);删除迭代器从start到end之间的元素
clear();删除容器所有的元素

注意:进行插入操作时,需要提供一个迭代器而不是普通的位置。

<2>数据访问/存取:
数据访问说明
ar(int index);返回index所指的数据
operator[];返回index所指的数据
front();返回容器第一个数据
back();返回容器最后一个数据
<3>容器互换:

互换容器是指两个容器内元素进行互换

容器互换说明
swap(vec);将vec与本身的元素进行互换
#include<iostream>
#include<vector>
using namespace std;

void test01(){
    vector<int> v;
    for(int i = 0; i < 100000; ++i){
        v.push_back(i);
    }
    cout << "v的容量capacity为:" << v.capacity() << endl;
    cout << "v的大小size为:" << v.size() << endl;
    //1.当重新指定大小后,出现内存浪费
    v.resize(3);
    cout << "v的容量capacity为:" << v.capacity() << endl;
    cout << "v的大小size为:" << v.size() << endl;
    //2.利用swap函数进行内存收缩
    vector<int>(v).swap(v);
    cout << "v的容量capacity为:" << v.capacity() << endl;
    cout << "v的大小size为:" << v.size() << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220321133643216

image-20220321133517751

注意:匿名对象在当前行代码执行完成后,编译器会立即回收内存。

<4>预留空间:

预留空间可以减少vector在动态扩展容量时的扩展次数。

预留空间说明
reserve(int len);容器预留len个元素长度(预留位置不初始化、元素不可访问)
#include<iostream>
#include<vector>
using namespace std;

void test01(){
    vector<int> v;
    //统计vector内存动态开辟次数
    int num = 0;
    int *p = NULL;
    for(int i = 0; i < 100000; ++i){
        v.push_back(i);
        if(p != &v[0]){
            p = &v[0];
            num++;
        }
    }
    cout << "vector内存动态开辟的次数为:" << num << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220321134507160

如上图,开辟vector动态开辟内存的次数过多,可以利用reserve预留空间来解决这种问题:

image-20220321134723435

image-20220321134742746

总结:如果数据量级比较大,可以一开始就利用reserve函数提前预留空间,防止vector多次开辟内存(提速)。

3.deque容器:

(1)deque双端数组:

双端数组可以对头尾两端进行插入、删除操作。

vector与deque的区别:

  1. vector对于头部的插入删除效率较低(数据量越大,效率越低)
  2. deque对于头部的插入删除速度更快
  3. vector访问元素时的速度会比deque更快(与两者的内部实现有关)

image-20220322093141899

deque工作原理:deque内部有一个中控器(存放缓冲区的地址),用于维护每段缓冲区中的内容(缓冲区中存放真实的数据)

image-20220322093402631

注意:deque容器的迭代器是支持随机访问的迭代器

(2)deque构造&赋值:
构造函数说明
deque<T> deqT;默认构造形式
deque(beg, end);构造函数将[beg, end)区间中的元素拷贝给本身
deque(n, elem);构造函数将n个elem拷贝给本身
deque(const deque &deq);拷贝构造函数
赋值函数说明
deque& operator=(const deque &deq);重载等号操作符
assign(beg, end);将[beg, end)区间中的数据拷贝赋值给本身
assign(n, elem);将n个elem拷贝给本身

deque的赋值方式与vector基本相似

(3)deque大小操作:
deque大小操作说明
empty();判断容器是否为空
size();返回容器中元素个数
resize(n);重写指定容器长度为n(默认值填充)
resize(n, elem);重写指定容器长度为n(以elem值填充)

注意:deque容器中没有获取capacity容量操作,因为deque没有容量的限制(可以无限的开辟空间/缓冲器)

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

void printDeque(const deque<int> &d){
    for(deque<int>::const_iterator it = d.begin(); it != d.end(); ++it){
        cout << *it << " ";
    }
    cout << endl;
}

void test01(){
    deque<int> d1;
    for(int i = 0; i < 10; ++i){
        d1.push_back(i);
    }
    printDeque(d1);
    cout << "deque是否为空:" << d1.empty() << endl;
    cout << "deque的size大小为:" << d1.size() << endl;
    d1.resize(15);
    cout << "dequeResize后的结果为:"; printDeque(d1);
    cout << "dequeResize后的大小为:" << d1.size() << endl;
    d1.resize(20, -1);
    cout << "dequeResize后的结果为:"; printDeque(d1);
    cout << "dequeResize后的大小为:" << d1.size() << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220322100517949

(4)deque常用操作:
<1>插入与删除:

deque从两端插入&删除操作:

两端操作说明
push_back(elem);
push_front(elem);
pop_back();
pop_front();

deque指定位置的插入&删除操作:

指定位置操作说明
insert(pos, elem);在pos位置插入elem元素的拷贝,返回新数据的位置
insert(pos, n, elem);在pos位置插入n个elem数据,无返回值
insert(pos, beg, end);在pos位置插入[beg, end)区间的数据,无返回值
clear();清空所有数据
erase(beg, end);删除指定区间,返回下一个数据位
erase(pos);删除指定位置,返回下一个数据位
<2>数据访问/存取:
数据访问说明
at(int index);返回索引index所指的数据
operator[];返回索引index所指的数据
front();返回容器中第一个元素
back();返回容器中最后一个元素
<3>deque排序:
排序函数说明
sort(iterator beg, iterator end);

注意:对于支持随机访问迭代器的容器,都可以使用sort算法直接对其进行排序。

4.综合运用1:评委打分

有5名选手(ABCDE),10个评委分别对每一名选手进行打分,取出最高分和最低分后求平均值分数

  1. 创建5名选手,存放到vector容器中
  2. 遍历vector容器,取出每一名选手执行for循环(将评委们的10个评分存放到deque容器中)
  3. 使用sort算法对deque容器中的分数排序后,取出最高分与最低分
  4. 对deque容器遍历累加总分,获取最后的平均分
#include<iostream>
#include<string>
#include<deque>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;

class Person{
public:
    Person(string n, int s){
        this->name = n;
        this->score = s;
    }
    string name;
    int score;
};

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

void setScore(vector<Person> &v){
    for(vector<Person>::iterator it = v.begin(); it != v.end(); ++it){
        deque<int> d;
        //1.将10个评委的分数存入deque容器中,并显示打分结果
        for(int i = 0; i < 10; ++i){
            int score = rand()%41 + 60;
            d.push_back(score);
        }
        cout << "选手:" << it->name << "\t打分:" << endl;
        for(deque<int>::iterator dit = d.begin(); dit != d.end(); ++dit){
            cout << *dit << " ";
        }
        cout << endl;
        //2.去掉最高分与最低分
        sort(d.begin(), d.end());
        d.pop_front();
        d.pop_back();
        //3.获取最后得分平均分
        int sum = 0;
        for(deque<int>::iterator dit = d.begin(); dit != d.end(); ++dit){
            sum += *dit;
        }
        int avg = sum / d.size();
        it->score = avg;
    }
}

void showScore(vector<Person> &v){
    for(vector<Person>::iterator it = v.begin(); it != v.end(); ++it){
        cout << "姓名:" << (*it).name << "\t平均分:" << it->score << endl;
    }
}

int main(){
    //随机数种子
    srand((unsigned int) time(NULL));
    //1.创建5名选手
    vector<Person> v;
    //2.初始化5名选手并添加到vector容器中
    createPerson(v);
    //3.给5名选手打分
    setScore(v);
    //4.显示最后得分
    showScore(v);

    system("pause");
    return 0;
}

image-20220322173140660

三、STLGroup2

1.stack容器:

(1)stack栈(FILO):

stack是一种先进后出(First In Last Out,FILO)的数据结构,只有一个出口:

image-20220322173349823

注意:栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历的行为(不支持随机访问)。

(2)stack常用接口:
构造函数说明
stack<T> stk;默认构造
stack(const stack &stk);拷贝构造
赋值操作说明
stack& operator=(const stack &stk);重载等号操作符
数据存取说明
push(elem);向栈顶添加元素
pop();从栈顶移出一个元素
top();返回栈顶元素
大小操作说明
empty();判断栈是否为空
size();返回栈的大小
#include<iostream>
#include<stack>
using namespace std;

int main(){
    stack<int> s;
    s.push(10);
    s.push(20);
    s.push(30);
    s.push(40);
    s.push(50);
    cout << "栈的大小为:" << s.size() << endl;
    while(!s.empty()){
        cout << "栈顶的元素为:" << s.top() << endl;
        s.pop();
    }
    cout << "栈的大小为:" << s.size() << endl;
    system("pause");
    return 0;
}

image-20220323111134167

生活中的栈Stack:

image-20220323111725217

2.queue容器:

(1)queue队列(FIFO):

stack是一种先进先出(First In First Out,FIFO)的数据结构,有两个出口:

image-20220323111343390

注意:

  1. 队列容器允许从一端新增元素,从另一端移出元素。
  2. 队列中只有队头和队尾的元素才可以被外界使用,因此队列不允许有遍历的行为(不支持随机访问)。
(2)queue常用接口:
构造函数说明
queue<T> que;默认构造
queue(const queue & que);拷贝构造
赋值操作说明
queue& operator=(const queue &que);重载等号运算符
数据存取说明
push(elem);队尾添加elem元素
pop();队头移出一个元素
back();返回队尾最后一个元素
front();返回队头的第一个元素
大小操作说明
empty();判断堆栈是否为空
size();返回栈的大小
#include<iostream>
#include<queue>
#include<string>
using namespace std;

class Person{
public:
    Person(string n, int a){
        this->name = n;
        this->age = a;
    }
    string name;
    int age;
};

int main(){
    queue<Person> q;
    Person p1("lch", 21);
    Person p2("yangzi", 18);
    Person p3("lixiain", 16);
    Person p4("wangyibo", 18);
    Person p5("xiaozhan", 25);
    q.push(p1);
    q.push(p2);
    q.push(p3);
    q.push(p4);
    q.push(p5);
    cout << "队列的大小为:" << q.size() << endl;
    while(!q.empty()){
        cout << "队头元素-姓名:" << q.front().name << "年龄:" <<  q.front().age << endl;
        cout << "队尾元素-姓名:" << q.back().name << "年龄:" <<  q.back().age << endl;
        q.pop();
    }
    cout << "队列的大小为:" << q.size() << endl;
    system("pause");
    return 0;
}

image-20220323114007330

生活中的队列Queue:

image-20220323112111765

3.list容器:

(1)list链表:

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

链表由一系列结点组成,而结点是由存储数据元素的数据域 and 存储下一个指针地址的指针域组成。

image-20220323122217688

注意:

  1. STL中的链表是一个双向循环链表
  2. 由于链表的存储方式并不是连续的内存空间,故链表中的迭代器属于双向迭代器,只支持前移and后移
链表优点
1.采用动态存储分配,不会造成内存浪费和溢出
2.链表执行插入和删除操作十分方便(修改指针即可,不需要移动大量元素)
3.插入和删除操作都不会造成原有list迭代器失效(这在vector是不成立的)
链表缺点
1.链表灵活但是空间(指针域)和时间(遍历)消耗较大
(2)list构造&赋值:
构造函数
list<T> lst;
list(beg, end);
list(n, elem);
list(const list &list)
赋值函数
assign(beg, end);
assign(n, elem);
list& operator=(const list &list);
swap(list);
#include<iostream>
#include<list>
using namespace std;

void printList(list<int> &l){
    for(list<int>::iterator it = l.begin(); it != l.end(); ++it){
        cout << *it << " ";
    }
    cout << endl;
}

void test01(){
    list<int> l1, l2, l3, l4;
    l1.push_back(10);
    l1.push_back(20);
    l1.push_back(30);
    l1.push_back(40);
    l1.push_back(50);
    l2 = l1;
    l3.assign(l2.begin(), l2.end());
    l4.assign(5, 60);
    printList(l1);
    printList(l2);
    printList(l3);
    printList(l4);
    swap(l1, l4);
    printList(l1);
    printList(l2);
    printList(l3);
    printList(l4);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220324144724021

(3)list大小操作:
大小操作
size();
empty();
resize(num);
resize(num, elem);
(4)list常用操作:
<1>插入和删除:
插入与删除
push_back(elem);
push_front(elem);
pop_back();
pop_front();
insert(pos, elem);
insert(pos, n, elem);
insert(pos, beg, end);
erase(beg, end);
erase(pos);
remove(elem);
clear();
<2>数据存取:
数据存取说明
front();返回第一个元素
back();返回最后一个元素

注意:list存储的方式并不是连续的空间,不允许使用[]at(index)的方式进行访问元素(迭代器不支持随机访问)。

list<int>::iterator it = L1.begin();
it++; it--;//支持双向操作
it = it + 1;//不支持随机访问
<3>反转和排序:
操作函数说明
reverse();反转链表元素
sort();对链表元素进行排序

注意:

  1. 对于sort函数,所有不支持随机访问迭代器的容器,不可以使用标准算法。
  2. 对于不支持随机访问迭代器的容器,内部会提供对应的一些算法(调用成员函数,而不是全局函数)。
#include<iostream>
#include<list>
using namespace std;

void printList(list<int> &l){
    for(list<int>::iterator it = l.begin(); it != l.end(); ++it){
        cout << *(it) << " ";
    }
    cout << endl;
}

bool myCompare(int num1, int num2){
    return num1 > num2;
}

void test01(){
    list<int> l1;
    l1.push_back(20);
    l1.push_back(30);
    l1.push_back(80);
    l1.push_back(50);
    l1.push_back(10);
    //1.sort默认排序为升序
    l1.sort();
    printList(l1);
    //2.实现sort降序排序
    l1.sort(myCompare);
    printList(l1);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220327223018395

<4>list排序案例:

需求描述:

  1. 将Person自定义数据类型进行排序,Person中的属性有姓名、年龄、身高
  2. 排序规则为:按照年龄进行升序排序,如果年龄相同则按照身高进行降序排序
#include<iostream>
#include<list>
#include<string>
using namespace std;

class Person{
public:
    Person(string n, int a, int h){
        this->name = n;
        this->age = a;
        this->height = h;
    }
    string name;
    int age;
    int height;
};

void printList(list<Person> &l){
    for(list<Person>::iterator it = l.begin(); it != l.end(); ++it){
        cout << "姓名:" << it->name << "\t年龄:" << it->age << "\t身高:" << it->height << endl;
    }
}

bool myCompare(Person p1, Person p2){
    if(p1.age == p2.age){
        return p1.height > p2.height;//年龄相同按照身高降序
    }else{
        return p1.age < p2.age;//年龄不同按照年龄升序
    }
}

int main(){
    list<Person> l;
    Person p1("lch", 19, 174);
    Person p2("uio", 22, 176);
    Person p3("okl", 25, 178);
    Person p4("qwe", 16, 171);
    Person p5("bnm", 19, 178);
    l.push_back(p1);
    l.push_back(p2);
    l.push_back(p3);
    l.push_back(p4);
    l.push_back(p5);
    printList(l);
    cout << "排序后:" << endl;
    l.sort(myCompare);
    printList(l);

    system("pause");
    return 0;
}

image-20220327234139054

4.set/multiset容器:

(1)set概念:

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

set与multiset区别:

  1. set不允许容器中有重复的元素,而multiset允许容器中有重复的元素(不会检测数据)。
  2. set插入数据的同时会返回插入结果,表示插入是否成功。
(2)set构造与赋值:
构造函数说明
set<T> s;默认构造函数
set(const set &s);拷贝构造函数
赋值函数说明
set& operator=(const set &s);重载等号运算符
#include<iostream>
#include<set>
using namespace std;

void printSet(set<int> &s){
    for(set<int>::iterator it = s.begin(); it != s.end(); ++it){
        cout << *it << " ";
    }
    cout << endl;
}

int main(){
    set<int> s1;
    s1.insert(10);
    s1.insert(20);
    s1.insert(30);
    s1.insert(30);
    s1.insert(40);
    //set容器特点:1.所有元素插入时自动排序、2.容器中的元素不允许重复
    printSet(s1);
    set<int> s2(s1);
    printSet(s2);
    set<int> s3;
    s3 = s2;
    printSet(s3);

    system("pause");
    return 0;
}

image-20220328002247696

(3)set大小与交换:
大小操作说明
size();返回容器中元素的数目
empty();判断容器是否为空
swap();交换两个集合容器
(4)set常用操作:
<1>插入与删除:
插入与删除说明
insert();向容器中插入元素
erase(pos);删除pos迭代器所指元素,返回下一个元素迭代器
erase(beg, end);删除区间[beg, end)迭代器所指元素,返回下一个元素迭代器
erase(elem);删除容器中值为elem的元素
clear();清除所有元素
<2>查找与统计:
查找与统计说明
find(key);查找key是否存在,若存在返回key的元素的迭代器,若不存在返回set.end()
count(key);统计key元素的个数
#include<iostream>
#include<set>
using namespace std;

int main(){
    set<int> s1;
    s1.insert(20);
    s1.insert(10);
    s1.insert(50);
    s1.insert(40);
    s1.insert(30);
    s1.insert(30);
    s1.insert(10);
    s1.insert(10);
    //find查找
    set<int>::iterator pos = s1.find(40);
    if(pos != s1.end()){
        cout << "找到元素:" << *pos << endl;
    }else{
        cout << "未找到元素" << endl;
    }
    //count统计
    int num = s1.count(10);
    cout << "num = " << num << endl;//对与set的count结果只能是0/1(不允许重复元素)

    system("pause");
    return 0;
}

image-20220330072007016

<3>排序:

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

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

class MyCompare{
public:
    bool operator()(int n1, int n2){
        return n1 > n2;
    }
};

template<typename T>
void printSet(set<int, T> &s){
    for(typename set<int, T>::iterator it = s.begin(); it != s.end(); ++it){
        cout << *it << " ";
    }
    cout << endl;
}

void test01(){
    set<int> s1;
    s1.insert(20);
    s1.insert(10);
    s1.insert(50);
    s1.insert(30);
    s1.insert(40);
    printSet(s1);

    set<int, MyCompare> s2;//在插入数据之前指定排序规则
    s2.insert(20);
    s2.insert(10);
    s2.insert(50);
    s2.insert(30);
    s2.insert(40);
    printSet(s2);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220330083959232

set容器对自定义数据类型的排序:

#include<iostream>
#include<set>
#include<string>
using namespace std;

class Person{
public:
    Person(string n, int a){
        this->name = n;
        this->age = a;
    }
    string name;
    int age;
};

class ComparePerson{
public:
    //引用方式传递参数不会导致拷贝构造的出现
    bool operator()(const Person &p1, const Person &p2){
        //按照年龄降序
        return p1.age > p2.age;
    }
};

void test01(){
    //自定义数据类型在插入前必须指定排序规则,可利用仿函数实现
    set<Person, ComparePerson> s;
    Person p1("lch", 18);
    Person p2("uio", 21);
    Person p3("bmn", 25);
    Person p4("qwe", 16);
    Person p5("zxc", 19);
    s.insert(p1);
    s.insert(p2);
    s.insert(p3);
    s.insert(p4);
    s.insert(p5);
    for(set<Person, ComparePerson>::iterator it = s.begin(); it != s.end(); ++it){
        cout << "姓名:" << it->name << "\t年龄:" << it->age << endl;
    }
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220330090547857

<4>pair对组创建:

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

创建方式说明
pair<type, type> p(value1, value2);默认构造
pair<type, type> p = make_pair(value1, value2);make_pair创建对组
#include<iostream>
using namespace std;

int main(){
    pair<string, int> p1("lch", 21);
    cout << "name = " << p1.first << "\tage = " << p1.second << endl;
    pair<string, int> p2 = make_pair("uio", 18);
    cout << "name = " << p2.first << "\tage = " << p2.second << endl;
    
    system("pause");
    return 0;
}

image-20220330073515277

5.map/multimap容器:

(1)map概念:

map中所有的元素都是pair,pair中第一个元素为key键值起索引作用、第二个元素为value实值

map/multimap(是否允许重复key)属于关联式容器,底层是用二叉树实现的。

注意:

  1. map中所有的元素会根据元素的键值自动排序
  2. 根据key值可以快速找到value值
(2)map构造与赋值:
构造函数说明
map<T1, T2> mp;默认构造函数
map(const map &mp);拷贝构造函数
赋值函数说明
map& operator=(const map &mp);重载等号运算符
#include<iostream>
#include<map>
using namespace std;

void printMap(map<int, int> &m){
    for(map<int, int>::iterator it = m.begin(); it != m.end(); ++it){
        cout << "key = " << (*it).first << "\tvalue = " << it->second << endl;
    }
}

void test01(){
    map<int, int> m;
    m.insert(pair<int, int>(3, 30));
    m.insert(pair<int, int>(1, 10));
    m.insert(pair<int, int>(4, 40));
    m.insert(pair<int, int>(2, 20));
    printMap(m);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220330092820067

(3)map大小与交换:
大小&交换说明
size();返回容器中元素的个数
empty();判断容器是否为空
swap();交换两个集合容器
(4)map常用操作:
<1>插入与删除:
插入与删除说明
insert(elem);在容器中插入元素
erase(pos);删除pos迭代器所指的元素,返回下一个元素迭代器
erase(beg, end);删除区间[beg, end)的所有元素,返回下一个元素的迭代器
erase(key);删除容器中值为key的元素
clear();清除所有元素
#include<iostream>
#include<map>
using namespace std;

void printMap(map<int, int> &m){
    for(map<int, int>::iterator it = m.begin(); it != m.end(); ++it){
        cout << "key = " << it->first << "\tvalue = " << it->second << endl;
    }
    cout << endl;
}

void test01(){
    map<int, int> m;
    m.insert(pair<int, int>(1, 10));
    m.insert(make_pair(2, 20));
    m.insert(map<int, int>::value_type(3, 30));
    m[4] = 40;
    cout << m[5] << endl;//自动创建元素默认值为0
    printMap(m);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220402203921502

<2>查找与统计:

对map容器进行查找数据以及统计数据

查找与统计说明
find(key);查找key是否存在,若存在返回元素的迭代器,否则返回set.end()
count(key);统计key的个数

注意:

  1. map不允许插入重复的key,count统计结果只能是0或者1
  2. multimap的count统计值可能大于1
<3>排序:

map容器默认排序规则都为按照key值进行从小到大排序,可以利用仿函数改变排序规则:

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

class MyCompare{
public:
    bool operator()(int n1, int n2){
        return n1 > n2;//按照降序排列
    }
};

template<typename T>
void printMap(map<int, int, T> &s){
    for(typename map<int, int, T>::iterator it = s.begin(); it != s.end(); ++it){
        cout << "key = " << it->first << "\tvalue = " << it->second << endl;
    }
    cout << endl;
}

void test01(){
    map<int, int> m1;
    m1.insert(make_pair(3, 100));
    m1.insert(make_pair(1, 90));
    m1.insert(make_pair(5, 50));
    m1.insert(make_pair(2, 20));
    m1.insert(make_pair(4, 45));
    printMap(m1);

    map<int, int, MyCompare> m2;
    m2.insert(make_pair(6, 50));
    m2.insert(make_pair(7, 21));
    m2.insert(make_pair(8, 78));
    m2.insert(make_pair(9, 98));
    printMap(m2);
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220402210446151

注意:

  1. 利用仿函数可以指定map容器的排序规则
  2. 对于自定义类型map必须要指定排序规则,否则报错(与set容器相同)

6.综合运用2:员工分组

需求分析:公司招聘了10名员工(ABCDEFGHIJ),10名员工进入公司之后需要指派员工工作的部门

  • 员工的信息主要有:姓名name、工资组成salary component
  • 公司部门主要有:策划、美术、研发
  • 随机给10名员工分配部门和工资,通过multimap进行信息的插入(key部门编号、value员工)
  • 需要分部门显示员工的信息

实现思路:

  1. 创建10名员工,存放到vector数组中
  2. 遍历vector容器取出每个员工,进行随机分组
  3. 分组后将员工部门编号作为key具体员工作为value,放到multimap容器中
  4. 分部门显示员工信息
#include<iostream>
#include<vector>
#include<map>
#include<ctime>
using namespace std;

#define CEHUA 0
#define MEISHU 1
#define YANFA 2

class Worker{
public:
    string name;
    int salary;
};

void createWorker(vector<Worker> &v){
    string nameSeed = "ABCDEFGHIJ";
    for(int i = 0; i < 10; ++i){
        Worker worker;
        worker.name = "员工";
        worker.name += nameSeed[i];
        worker.salary = rand()%10000 + 10000;//10000~19999
        v.push_back(worker);
    }
}

void setGroup(vector<Worker> &v, multimap<int, Worker> &m){
    //遍历每一个员工进行分组操作
    for(vector<Worker>::iterator it = v.begin(); it != v.end(); ++it){
        //1.产生随机的部门编号
        int deptId = rand()%3;//0 1 2
        //2.将员工插入到分组中
        m.insert(make_pair(deptId, *it));
    }
}

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

int main(){
    srand((unsigned int)time(NULL));
    //1.创建员工
    vector<Worker> vWorker;
    createWorker(vWorker);
    //2.实现员工分组
    multimap<int, Worker> mWorker;
    setGroup(vWorker, mWorker);
    //3.分组显示员工
    showWorkerByGroup(mWorker);
    /*
    for(vector<Worker>::iterator it = vWorker.begin(); it != vWorker.end(); ++it){
        cout << "姓名:" << it->name << "\t工资:" << it->salary << endl;
    }
    cout << endl;
    */
    system("pause");
    return 0;
}

image-20220402214557462

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

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

相关文章

前端性能优化(三):代码优化

目录 一&#xff1a;JS开销以及如何缩短解析时间 二&#xff1a;配合V8有效优化代码 2.1.抽象语法树 2.2.V8优化机制 三&#xff1a;函数优化 四&#xff1a;对象优化 4.1.以相同顺序初始化对象成员&#xff0c;避免隐藏类的调整 4.2.实例化后避免添加新属性 4.3.尽量…

数学建模经验分享

今天给大家分享一期关于数学建模比赛的经验分享&#xff0c;我将从以下三个方面展开说明&#xff1a; &#xff08;1&#xff09;如何准备数学建模比赛&#xff1f; &#xff08;2&#xff09;如何选择合适的赛题进行建模&#xff1f; &#xff08;3&#xff09;如何提高获奖…

辉哥带你学hive第三讲

文章目录 1.函数1.1系统内置函数1.2 单行函数1.2.1 算术运算函数1.2.2字符串函数1.2.3 日期函数1.2.4 流程控制函数1.2.5集合函数1.3高级聚合函数1.函数 1.1系统内置函数 1)查看系统自带的函数 hive> show functions; 2)显示自带的函数的用法 hive> desc function up…

机器学习HW10对抗性攻击

机器学习HW10对抗性攻击一、任务描述二、算法1、FGSM2、I-FGSM3、MI-FGSM4、多种输入&#xff08;DIM&#xff09;评估指标三、实验1、Simple Baseline2、Medium Baseline3、Strong Baseline4、Boss Baseline一、任务描述 我们使用pytorchcv来获得CIFAR-10预训练模型&#xff…

STP基础

名词&#xff1a; STP&#xff08;Spanning Tree Protocol&#xff09;生成树协议&#xff1a;运行STP协议的设备通过彼此交互信息发现网络中的环路&#xff0c;并有选择地对某个接口进行阻塞&#xff0c;最终将有环路的网络结构修剪成无环路的树形结构&#xff0c;从而防止报…

ENVI_IDL:对于书写和创建GEOTIFF结构体?

大家在使用WRITE_TIFF函数和READ_TIFF函数的时候&#xff0c;应该遇见过GEOTIFF参数。 但是我对于这个参数却是了解不深&#xff0c;趁着这次学习梳理一下GEOTIFF参数. 在学习之前&#xff0c;我发现IDL教程并没有提供关于GEOTIFF的示例&#xff0c;所以我尝试使用READ_TIFF函…

[Kettle] CSV文件输入

CSV(Comma-Separated Values)文件是以字符(大多数使用逗号,)分隔值&#xff0c;以纯文本形式存储数据的文件 数据源 语文成绩(kettle数据集1).csv https://download.csdn.net/download/Hudas/87356192?spm1001.2014.3001.5501 1.建立【CSV文件输入】转换工程 使用Ctrl N快…

如何将PDF转换为Excel?免费PDF转Excel方法分享

如何免费将PDF转换为Excel 像将文件转换为电子表格这样简单的事情应该不会那么困难。PDF已成为共享文档的标准格式&#xff0c;那么当您需要将PDF作为电子表格时&#xff0c;如何将PDF转换为Excel&#xff1f; 一些网站可以免费将PDF转换为Excel&#xff0c;尽管涉及一些联系…

力扣sql入门篇(五)

力扣sql入门篇(五) 1 组合两个表 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 SELECT firstname,lastname,IFNULL(city,null) city,IFNULL(state,null) state FROM Person p LEFT JOIN Address a ON p.personida.personid;1.3 运行截图 2 进店却未…

网络原理3 IP地址

网络原理3 IP地址 文章目录网络原理3 IP地址IP协议的报文格式IP地址的具体规则IP地址的组成子网掩码特殊的IP地址IP地址短缺的解决方法动态分配IP地址NAT机制[主流机制]IPv6路由选择网络层中主要做的事情是在两点之间规划出一个合理的路径&#xff0c;同时也要对主机所处的位置…

CleanMyMac X免费吗?怎么下载2023最新版

CleanMyMac X是一款专业的Mac清理软件&#xff0c;可智能清理mac磁盘垃圾和多余语言安装包&#xff0c;快速释放电脑内存&#xff0c;轻松管理和升级Mac上的应用。同时CleanMyMac X可以强力卸载恶意软件&#xff0c;修复系统漏洞&#xff0c;一键扫描和优化Mac系统&#xff01;…

哈希问题详解

什么是哈希表 在引入哈希表之前&#xff0c;先谈一下为什么要了解哈希表。在学习Set集合时&#xff0c;发现Set集合可以实现无序存储&#xff0c;那么Set是如何实现的无序存储&#xff1f; 打开源码会发现Set集合的底层实际上是由一个map集合实现的。那么什么是哈希表呢&…

【人工智能原理自学】一元一次函数感知器:如何描述直觉

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;笔记来自B站UP主Ele实验室的《小白也能听懂的人工智能原理》。 &#x1f514;本文讲解一元一次函数感知器&#xff1a;如何描述直觉&#xff0c;一起卷起来叭&#xff01; 目录…

基于Vue和SpringBoot的便利店仓库物资管理系统的设计与实现

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

jvm内存模型简介

一、Jvm 的介绍 1、JVM体系结构 2、JVM运行时数据区 3、JVM内存模型 JVM运行时内存 共享内存区 线程内存区 3.1、共享内存区 共享内存区 持久带(方法区 其他) 堆(Old Space Young Space(den S0 S1)) 持久代&#xff1a; JVM用持久带&#xff08;Permanent Space&…

Java -- 软件开发整体流程;项目环境dev,test,staging,prod

软件开发整体介绍 作为一名软件开发工程师&#xff0c;我们需要了解在软件开发过程中的开发流程&#xff0c; 以及软件开发过程中涉及到的岗位角色&#xff0c;角色的分工、职责&#xff0c; 并了解软件开发中涉及到的四种软件环境。我们将从 软件开发流程、角色分工、软件环境…

7-4 乘法口诀数列

本题要求你从任意给定的两个 1 位数字 a1​ 和 a2​ 开始&#xff0c;用乘法口诀生成一个数列 {an​}&#xff0c;规则为从 a1​ 开始顺次进行&#xff0c;每次将当前数字与后面一个数字相乘&#xff0c;将结果贴在数列末尾。如果结果不是 1 位数&#xff0c;则其每一位都应成为…

1015:计算并联电阻的阻值(信奥赛一本通)

题目跳转&#xff1a;点击这里 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 【输入】 两个电阻阻抗大小&#xff0c;浮点型&#xff0c;以一个空格分开。 【输出】 并联之后的阻抗大小&#xff0c;结果保留小数点后22位。 【输入样例】 1 2 【输出样例】 0.67 …

Windows下Cmake的简易工程构建

新建两个文件head,src&#xff0c;用于存放头文件和c文件。 再新建CMakeLists.txt文件&#xff0c;用于cmake配置。 当前文件结构: --->CMakeLists.txt | --->head | --->src新建一个头文件hello.h 内容如下: #ifndef HELLO_H #define HELLO_H #include "stdio…

Koxia and Number Theory(数论)

题目链接&#xff1a; Problem - C - Codeforces 题目大意&#xff1a; 给定一个数组a.问是否存在x,使得gcd(aix,ajx)1 对任意(1<x<j<n)成立 思路&#xff1a; 首先不难发现&#xff0c;数组不可以出现相同的数字 记biaix 要满足gcd(bi,bj)1 对任意(1<x<…