C++基础学习笔记(下)

C++基础学习笔记(下)

文章目录

一、模板

(一)函数模板

  • C++的另一种编程思想为泛型编程,主要利用模板技术。

  • C++提供两种模板机制:函数模板类模板

1.函数模板语法

函数模板的作用:建立一个通用函数,其函数返回值类型形参类型可以不具体指定,用一个虚拟的类型来代表。

语法:使用关键字:template

template<typename T>
函数声明或定义
  • template:声明创建模板;

  • typename:表明其后面的符号是一种数据类型,可以用class代替;

T:通用的数据类型,名称可以替换,通常为大写字母;

示例:交换两个未知类型的数

//声明一个模板,告诉编译器后面的 T 是一个通用数据类型
template <typename T> 
void swapNum(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
    cout << "a:" << a << endl;
    cout << "b:" << b << endl;
}
void test(){
    int a = 10;
    int b = 20;
    //第1种:自动类型推导
    swapNum(a, b);
    //第2种:显式指定类型
    swapNum <int>(a, b);
}

2.函数模板注意事项

(1)自动类型推导时,必须推导出一致的数据类型T才可以使用

(2)使用模板时必须确定出T的数据类型才可以使用。若不指定T数据类型则不能调用函数。

3.函数模板示例

封装一个选择排序算法从大到小对不同数据类型的数组进行排序。使用char数组和int数组测试。

template <class T>
void swapNums(T &a, T &b) {
    T temp = a;
    a = b;
    b = temp;
}

template <class T>
void printArr(T arr[], int len){
    for (int i = 0; i < len; ++i) {
        cout << arr[i] << " " << endl;
    }
}

template <class T>
void selectSort(T arr[], int len){
    for (int i = 0; i < len; ++i) {
        int max = i;//认定最大值的下标
        for (int j = i+1; j < len; ++j) {
            //若认定的最大值比遍历出的数值要小,说明j下标的元素才是最大值
            if (arr[max] < arr[j]){
                max = j;
            }
        }
        if (max != i){
            //交换max和i的元素
            swapNums(arr[max], arr[i]);
        }
    }
}

void sortTest(){
    //输入字符串数组
    char charArr[] = "agfcvd";
    int num = sizeof(charArr) / sizeof(char);
    selectSort(charArr, num);
    printArr(charArr, num);
    //输入int数组
    int arr[] = {12,4,1,0,6,3};
    int num1 = sizeof(arr) / sizeof(int);
    selectSort(arr, num1);
    printArr(arr, num1);
}

4.普通函数和函数模板的区别

(1)普通函数调用时可以自动进行类型转换(隐式类型转换),如将字符型变量变为ASCII码的int型数值;

(2)函数模板调用时,如果用自动类型推导,不会发生隐式类型转换,而显式指定类型的方式可以发生隐式类型转换。

5.普通函数和函数模板的调用规则

(1)优先调用普通函数;

(2)可以通过空模板参数列表来强制调用函数模板;

(3)函数模板可以发生重载;

(4)若函数模板可以产生更好的匹配则优先调用函数模板。

void printNum(int a, int b){
    cout << "调用函数" << endl;
}

template <class T>
void printNum(T a, T b){
    cout << "调用模板函数" << endl;
}

template <class T>
void printNum(T a, T b, T c){
    cout << "调用重载模板函数" << endl;
}

int main(){
    int a = 1, b = 2, c = 0;
    //(1)优先调用普通函数
    printNum(a, b);
    //(2)通过空模板参数列表强制调用模板函数
    printNum<>(a, b);
    //(3)调用重载模板函数
    printNum(a, b, c);
    //(4)虽然普通函数可以隐式类型转换,但匹配模板函数更好
    char d = 'd',e = 'e';
    printNum(d, e);
}

6.模板的局限性

对于某些特定数据类型需要用具体化方式做特殊实现。示例:比较两个Person类

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

template <class T>
bool compare(T &a, T &b){
    if(a == b){
        return true;
    } else{
        return false;
    }
}
//第1种:可以采用运算符重载
//第2种:利用具体化Person的版本实现
template <> bool compare(Person &a, Person &b){
    if(a.m_name == b.m_name && a.m_age == b.m_age){
        return true;
    } else{
        return false;
    }
}

void test(){
    Person p1("小a", 10);
    Person p2("小b", 20);
    int result = compare(p1, p2);
    if (result == true){
        cout << "相等!" << endl;
    } else{
        cout << "不等!" << endl;
    }
}

(二)类模板

1.类模板作用和语法

(1)作用:建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟类型来代表。

(2)语法:

template <typename T>

2.类模板和函数模板区别

(1)类模板没有自动类型推导方式;

(2)类模板在模板参数列表中可以有默认参数。

template <class NameType, class AgeType = int>//类模板中,可以指定默认类型参数
class Person1{
public:
    Person1(NameType name, AgeType age){
        this->mName = name;
        this->mAge = age;
    }
    void showInfo(){
        cout << this->mName << endl;
        cout << this->mAge << endl;
    }
    NameType mName;
    AgeType mAge;
};

int main(){
    //此行错误!不能使用自动类型推导
//    Person p0("wzh", 10);
    //应该显式指定类型
    Person1 <string> p1("wzh", 10);
    p1.showInfo();
}

3.类模板中的成员函数创建时机

类模板普通类中的成员函数创建时机不同:普通类的成员函数一开始可以创建,类模板中的成员函数在调用时才创建。

class Person1{
public:
    void showInfo1(){
        cout << "show person1" << endl;
    }
};
class Person2{
public:
    void showInfo2(){
        cout << "show person2" << endl;
    }
};

template <class T>
class MyClass{
public:
    T obj;
    //类模板中的成员函数
    void func1(){
        obj.showInfo1();
    }
    void func2(){
        obj.showInfo2();
    }
};

int main(){
    MyClass <Person1> m;
    m.func1();
//    m.func2();此步报错,函数调用时才会创建成员函数
}

4.类模板对象作为函数参数

类模板实例化出的对象向函数传参公有三种传入方式:(主要用第一种)

1)指定传入的类型:直接显示对象的数据类型;

(2)参数模板化:将对象中的参数变为模板进行传递;

(3)整个类模板化:将这个对象类型模板化进行传递。

template <class T1, class T2>
class Person{
public:
    Person(T1 name, T2 age){
        this->mName = name;
        this->mAge = age;
    }
    void showInfo(){
        cout << "姓名:" << this->mName << "年龄:" << this->mAge << endl;
    }
    T1 mName;
    T2 mAge;
};
//第1种:指定传入的类型
void printPerson1(Person<string, int>&p){
    p.showInfo();
}

//第2种:参数模板化
template <class T1, class T2>
void printPerson2(Person<T1, T2>&p){
    p.showInfo();
}

//第3种:整个类模板化
template <class T>
void printPerson3(T &p){
    p.showInfo();
}

int main(){
    Person<string, int>p("w", 20);
    printPerson1(p);
    printPerson2(p);
    printPerson3(p);
}

5.类模板和继承

​ 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型;若不指定,编译器无法给子类分配内存;若想灵活指定出父类中T的类型,子类也需变为类模板。

template <class T>
class Base{
    T m;
};
class Son1:public Base<int>{//1.此处须指定父类的T数据类型

};
//2.想灵活指定父类中的T类型,子类须变为类模板
template <class T1, class T2>
class Son2:public Base<T2>{
public:
    Son2(){
        cout << "T1类型:" << typeid(T1).name() << endl;//输出 int
        cout << "T2类型:" << typeid(T2).name() << endl;//输出 char
    }
};
int main(){
    Son2<int, char>s2;
}

6.类模板成员函数类外实现

需要加上模板参数列表:

template <class T1, class T2>
class Person{
public:
    Person(T1 name, T2 age);//构造函数
    void showInfo();//成员函数
    T1 mName;
    T2 mAge;
};

//构造函数 类外实现
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age){
    this->mName = name;
    this->mAge = age;
}
//构造函数 类外实现
template <class T1, class T2>
void Person<T1, T2>:: showInfo(){
    cout << "名字:" << this->mName << endl;
    cout << "年龄:" << this->mAge << endl;
}

int main(){
    Person<string, int> p("wzh", 18);
    p.showInfo();
}

7.类模板份文件编写

​ 由于类模板中成员函数创建时机是在调用阶段,导致分文件(.cpp .h)编写时会链接不到,因此有两种解决方式:(1)直接包含.cpp源文件(不推荐);(2)将声明和实现写到同一个文件中,后缀改为.hpp(约定的后缀)。

8.类模板和友元

  • 全局函数类内实现:直接在类内声明友元即可;

  • 全局函数类外实现:需要提前让编译器知道全局函数的存在。

//1.通过全局函数打印信息
template <class T1, class T2>
class Person{
    //1.全局函数 类内实现
    friend void printInfo(Person<T1, T2>p){
        cout << "名字:" << p.mName << endl;
        cout << "年龄:" << p.mAge << endl;
    }

public:
    Person(T1 name, T2 age){
        this->mName = name;
        this->mAge = age;
    }
private:
    T1 mName;
    T2 mAge;
};

int main(){
    //1.类内调用
    Person<string, int>p1("tom", 22);
    printInfo(p1);
}
//类外实现,需要提前让编译器知道Person类存在
template <class T1,class T2>
class Person;

//通过全局函数打印信息
template <class T1,class T2>
void printInfo(Person<T1, T2>p){
    cout << p.mName << endl;
    cout << p.mAge << endl;
}


template <class T1, class T2>
class Person{
    //2.全局函数 类外实现,要加空模板参数列表
    //全局函数类外实现,需要让编译器提前知道这个函数的存在
    friend void printInfo<>(Person<T1, T2>p);
public:
    Person(T1 name, T2 age){
        this->mName = name;
        this->mAge = age;
    }
private:
    T1 mName;
    T2 mAge;
};

int main(){
    //类外实现
    Person<string, int>p("tom", 22);
    printInfo(p);
}

9.类模板案例

(1)目的:实现一个通用的数组类

​ 可以对内置数据类型以及自定义数据类型的数据进行传输;

​ 将数组中的数据存储到堆区;

​ 构造函数中可以传入数组的容量

​ 提供对应的拷贝构造函数以及operator=防止浅拷贝问题;

​ 提供尾插法尾删法对数组中的数据进行增加和删除;

​ 可以通过下标的方式访问数组中的元素;

​ 可以获取数组中当前元素个数和数组容量。

(2)思路:

​ 首先,类中有T * pAddr,返回新开辟的T类型数据的指针,维护数组中的数据;有容量mCapa,有mSize。

​ 其次,提供构造函数接收容量;提供拷贝构造函数和operator=实现深拷贝防止浅拷贝;利用下标访问数组中元素的方法等。

(2)实现:

//这是MyArr.hpp文件
template <class T>
class MyArr{
public:
    MyArr(int capa){
//        cout << "有参构造函数调用" << endl;
        this->mCapa = capa;
        this->mSize = 0;
        this->pAddr = new T[this->mCapa];//开辟堆区空间
    }

    //拷贝构造
    MyArr(const MyArr& arr){
//        cout << "拷贝构造函数调用" << endl;
        this->mCapa = arr.mCapa;
        this->mSize = arr.mSize;
        this->pAddr = arr.pAddr;//会产生浅拷贝
        //改为以下,深拷贝
        this->pAddr = new T[arr.mCapa];
        //拷贝arr中的数据
        for (int i = 0; i < this->mSize; ++i) {
            this->pAddr[i] = arr.pAddr[i];
        }
    }

    //operator=防止浅拷贝问题
    MyArr&operator=(const MyArr& arr){
//        cout << "operator调用" << endl;
        //先判断原来堆区是否有数据,有则释放
        if (this->pAddr != NULL){
            delete [] this->pAddr;
            this->pAddr = NULL;
            this->mCapa = 0;
            this->mSize = 0;
        }
        this->mCapa = arr.mCapa;
        this->mSize = arr.mSize;
        this->pAddr = new T[arr.mCapa];
        for (int i = 0; i < mSize; ++i) {
            this->pAddr[i] = arr.pAddr[i];
        }
        return *this;
    }
    //尾插法
    void pushBack(const T &val){
        //判断容量是否等于大小
        if (this->mCapa == this->mSize){
            return;
        }
        //插到数组中最后一个位置
        this->pAddr[this->mSize] = val;
        //更新数组的大小
        this->mSize ++;
    }

    //尾删法
    void popBack(){
        //让用户访问不到最后一个元素,逻辑删除
        if (this->mSize == 0){
            return;
        }
        this->mSize --;
    }

    //因为是自定义的模板类,不能通过数组下标访问元素
    //下面实现通过下标访问数组中的元素
    //函数调用要作为左值存在,要返回引用
    T& operator[](int index){
        return this->pAddr[index];
    }

    //返回数组容量
    int getCapa(){
        return this->mCapa;
    }
    //返回数组大小
    int getSize(){
        return this->mSize;
    }

    //析构函数
    ~MyArr(){
//        cout << "析构函数调用" << endl;
        if(this->pAddr != NULL){
            delete [] this->pAddr;
            this->pAddr = NULL;
        }
    }
private:
    T * pAddr;
    int mCapa;//数组容量
    int mSize;//传入数组大小
};
//这是主文件
#include "MyArr.hpp"
//自定义数据类型
class Person{
public:
    Person(){}
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    string mName;
    int mAge;
};

//打印int数组元素
void printIntArr(MyArr<int>& arr){
    for (int i = 0; i < arr.getSize(); ++i) {
        cout << arr[i] << endl;
    }
}
//打印自定义数据类型数组元素
void printPersonArr(MyArr<Person>& arr){
    for (int i = 0; i < arr.getSize(); ++i) {
        cout << arr[i].mName << endl;
        cout << arr[i].mAge << endl;
    }
}


int main(){
    MyArr<int>arr1(5);//测试1

    for (int i = 0; i < 5; ++i) {
        arr1.pushBack(i);//尾插法向数组添加数据
    }
    printIntArr(arr1);
    cout << "arr1容量" << arr1.getCapa() << endl;
    cout << "arr1大小" << arr1.getSize() << endl;

    MyArr<int>arr2(arr1);//测试2
    printIntArr(arr2);
    arr2.popBack();
    cout << "arr2容量" << arr2.getCapa() << endl;//输出 5
    cout << "arr2大小" << arr2.getSize() << endl;//输出 4

    MyArr<int>arr3(100);//测试3
    arr3 = arr1;//测试4

    //测试1、2、3、4行的输出:
    //有参构造函数调用
    //拷贝构造函数调用
    //有参构造函数调用
    //operator调用
    //析构函数调用
    //析构函数调用
    //析构函数调用

    //——————————测试自定义数据类型——————————
    MyArr<Person> arr(10);
    Person p1("小A", 10);
    Person p2("小B", 15);
    Person p3("小C", 13);
    Person p4("大D", 16);
    Person p5("大E", 35);
    Person p6("小A", 8);
    //将数据插入到数组中
    arr.pushBack(p1);
    arr.pushBack(p2);
    arr.pushBack(p3);
    arr.pushBack(p4);
    arr.pushBack(p5);
    arr.pushBack(p6);
    //打印数组
    printPersonArr(arr);
    cout << "arr容量" << arr.getCapa() << endl;//输出arr容量10
    cout << "arr大小" << arr.getSize() << endl;//输出arr大小6
		//——————————测试完成——————————
}

二、STL基础

STL:标准模板库,广义上分为容器算法迭代器,容器和算法之间通过迭代器连接,几乎所有的代码都采用多了模板类或模板函数。

(一)STL六大组件

(1)容器:各种数据结构,如vector、list、deque、set、map,用来存放数据;

(2)算法:各种常用的算法,如sort、find、copy、for_each等;

(3)迭代器:扮演了容器与算法之间的胶合剂;

(4)仿函数:行为类似函数,可作为算法的某种策略;

(5)适配器:一种用来修饰同期或者仿函数或迭代器借口的东西;

(6)空间配置器:负责空间的配置与管理。

(二)容器、算法、迭代器

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

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

(2)算法分为:

  • 质变算法:指运算过程中会更改区间内的元素的内容,如拷贝、替换、删除;

  • 非质变算法:指运算过程中不会更改区间内的元素内容,如查找、计数、遍历、寻找极值等。

(3)迭代器提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露容器内部表示方式。每个容器有自己专属的迭代器,使用类似于指针。

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

1.容器:verctor

(1)三种方式遍历容器中的数据:

int main() {
    //创建一个vector容器数组
    vector<int> v;
    v.push_back(1);
    v.push_back(4);
    v.push_back(2);

    //第一种遍历方式:通过迭代器访问容器中的数据
    vector<int>::iterator itBegin = v.begin();//起始迭代器,指向容器中第一个元素
    vector<int>::iterator itEnd = v.end();//结束迭代器,指向容器中的最后一个元素的下一个位置
    while (itBegin != itEnd){
        cout << *itBegin <<endl;
        itBegin ++;
    }
    //第二种遍历方式:通过for循环
    for (vector<int>::iterator it = v.begin();it != v.end();it++){
        cout << *it << endl;
    }

    //第三种遍历方式:利用自带的for_each函数
    for_each(v.begin(),v.end(),printInfo);



    return 0;
}

2.vector存放自定义数据类型

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    string mName;
    int mAge;
};
//——————————vector存放自定义数据类型并遍历——————————
void myDataType(){
    vector<Person> v;
    Person p1("小明",12);
    Person p2("小花",16);
    Person p3("小蓝",13);
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    //遍历
    for (vector<Person>::iterator it = v.begin();it != v.end();it ++){
        cout << "姓名:" << (*it).mName << "  年龄:" << (*it).mAge << endl;//通过解引用方式
        cout << "姓名:" << it->mName << "  年龄:" << it->mAge << endl;//通过指针方式

    }
}
//——————————存放自定义数据类型指针并遍历——————————
void myDataTypePoint(){
    vector<Person*> v;
    Person p1("a",12);
    Person p2("b",16);
    Person p3("c",13);
    v.push_back(&p1);
    v.push_back(&p2);
    v.push_back(&p3);
    //遍历
    for (vector<Person*>::iterator it = v.begin();it != v.end();it ++){
        cout << "姓名:" << (**it).mName << "  年龄:" << (**it).mAge << endl;//通过解引用方式
        cout << "姓名:" << (*it)->mName << "  年龄:" << (*it)->mAge << endl;//通过指针方式
    }
}

3.vector嵌套vector

int main(){
    vector<vector<int>>v;
    //创建小容器
    vector <int>v1;
    vector <int>v2;
    vector <int>v3;
    vector <int>v4;
    //向小容器中插入数据
    for (int i = 0; i < 4; ++i) {
        v1.push_back(i+1);
        v2.push_back(i+2);
        v3.push_back(i+3);
        v4.push_back(i+4);
    }
    //将小容器插入到大容器中
    v.push_back(v1);
    v.push_back(v2);
    v.push_back(v3);
    v.push_back(v4);
    //通过大容器,遍历所有数据
    for (vector<vector<int>>::iterator it = v.begin();it != v.end();it++){
        //(*it)是容器vector<int>
        for(vector<int>::iterator vit = (*it).begin();vit != (*it).end();vit++){
            cout << *vit << " ";
        }
        cout << endl;
    }
}

三、STL常用容器

(一)string容器

1.string基本概念

​ string本质上是一个类,和char* 的区别是:char是一个指针,string是一个类,类内部封装了char管理这个字符串,是一个char类型的容器。string内部封装了很多成员方法,如find、copy、delete、replace、insert等。string管理char所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责。str.size()返回字符串长度。

2.string构造函数

构造函数的原型:

  • string:创建一个空的字符串
  • string(const char* s):使用字符串s初始化
  • string(const string& str):使用一个string对象初始化另一个string对象
  • string(int n, char c):使用n个字符c初始化
//第1种
string s1;
//第2种
const char * str = "wzh";
string s2(str);
cout << "s2:" << s2 << endl;
//第3种
string s3(s2);
cout << "s3:" << s2 << endl;
//第4种
string s4(10, 'a');
cout << "s4:" << s4 << endl;//输出10个a

3.string赋值操作

给string字符串赋值的函数原型:

  • string& operator=(const char* s); //char*类型的字符串赋值给当前字符串
  • string& operator=(const string &s); //把字符串s赋给当前字符串
  • string& operator=(char c);//字符赋给当前的字符串
  • string& assign(const char *s); //把字符串s赋给当前的字符串
  • string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串
  • string& assign(const string &s); //把字符串s赋给当前字符串
  • string& assign(int n, char c); //把n个字符c赋给当前字符串

4.字符串拼接

在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 &s); //同operator+=(const string& str)
  • string& append(const string &s, int pos, int n); //字符串s中中pos开始的n个字符连接到字符串结尾

5.字符串查找和替换

函数原型:

  • int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找
  • int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始查找
  • int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置
  • int find(const char c, int pos = 0) const;//查找字符c第一次出现位置
  • int rfind(const string& str, int pos=npos) const;//查找str最后一次位置,从pos开始找
  • int rfind(const char* s, int pos=npos) const;//查找s最后一次位置,从pos开始找
  • int rfind(const char *s, int pos, int n) const;//从pos查找s的前n个字符最后一次位置
  • int rfind(const char c, int pos=0) const;//查找字符c最后一次出现位置
  • string& replace(int pos, int n, const string& str);//替换从pos开始n个字符为字符串str
  • string& replace(int pos, int n, const char* s);//替换从pos开始的n个字符为字符串s

6.字符串的比较

按照ASCII进行比较,=返回0,>返回1,<返回-1.

函数原型:

  • int compare(const string& s) const; //与字符串s比较
  • int compare(const char *s) const; //与字符串s比较

7.字符存取

string中单个字符存取:

  • char& operator[] (int n);//通过[]方式取字符
  • char& at(int n); //通过at方法获取字符

8.字符串插入和删除

函数原型:

  • string& insert(int pos, const char* s); //插入字符串
  • string& insert(int pos, const string& str); //插入字符串
  • string& insert(int pos, int n, char c); //在指定位置插入n个字符
  • string& erase(int pos, int n = npos); //删除从pos开始的n个字符

9.子串获取

函数原型:

​ string substr(int pos = 0, int n = npos) const; //返回由pos开始的n个字符组成的字符串

(二)vector容器

1.vector基本概念

​ vector数据结构和数组非常相似,也称为单端数组。与普通数组不同在于数组是静态空间,而vector可以动态扩展,即并不是在原空间之后续接新空间,而是找更大的内存空间,将原有数据拷贝到新空间,释放原空间,在插入数据过程中会不断开辟新空间,可以利用reserve()预留空间。vector容器的迭代器是支持随机访问的迭代器。

2.vector构造函数

  • vector<T> v;//采用模板实现类实现,默认构造函数
  • vector(v.begin(), v.end())//将v[begin(), end())区间(前闭后开)中的元素拷贝给自身
  • vector(n, elem);//构造函数将n个elem拷贝给自身
  • vector(const cector &vec)//拷贝构造函数
void printVector(vector<int>&v){
    for (vector<int>::iterator it = v.begin();it != v.end(); it++){
        cout << *it << endl;
    }
    cout << endl;
}

int main(){
    //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, 100);
    printVector(v3);//输出10个100
    
    //4.拷贝构造
    vector<int>v4(v3);
    printVector(v4);
}

3.vector赋值操作

函数原型:

  • vector& operator=(const vector &vec);//重载等号操作符
  • assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给自身
  • assign(n, elem);//将n个elem拷贝赋值给自身
vector<int>v1;
    for (int i = 0; i < 10; ++i) {
        v1.push_back(i+1);
    }
    printVector(v1);

    //1.operator=赋值
    vector<int>v2;
    v2 = v1;

    //2.assign赋值
    vector<int>v3;
    v3.assign(v1.begin(), v1.end());
    
    //3.assign进行n个elem赋值
    vector<int>v4;
    v4.assign(3,20);

4.vector容量和大小

函数原型:

  • empty();//判断容器是否为空
  • capacity();//容器的容量
  • size();//返回容器中元素的个数
  • resize(int num);//重新指定容器的长度为num,容器变长则以默认值0填充新位置,变短则删除超出部分
  • resize(int num, elem);//重新制定容器长度num,变长则以elem填充新位置,变短则删除超出部分

5.vector的插入和删除

函数原型:

  • push_back(ele);//尾部插入元素ele
  • pop_back();//删除最后一个元素
  • insert(const_iterator pos, ele);//迭代器指向位置pos插入元素ele
  • insert(const_iterator pos, intcount, ele);//迭代器指向位置pos插入count个元素ele
  • erase(const_iterator start, const_iterator end);//删除迭代器指向的元素
  • erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
  • clear();//删除容器中所有元素

6.vector数据存取

函数原型:

  • at(int idx);//返回索引idx所指的数据
  • operator[]//返回索引idx所指的数据,直接使用[]返回数据
  • front();//返回容器中第一个数据元素
  • back();//返回容器中最后一个数据元素

7.vector互换容器

函数原型:swap(vec);//将vec与本身的元素互换

通过创建匿名对象可以用来收缩vector占用的空间:vector<int>(v).swap(v)

8.vector预留空间

通过预留空间减少vector动态扩展时的扩展次数。

函数原型:reserve(int n);//容器预留len个元素长度,预留位置不初始化,元素不可访问。

(三)deque容器

1.deque基本概念

​ deque是双端数组。vector对于头部的插入删除效率低,数据量越大效率越低。而deque对头部的插入删除速度快,但vector访问元素的速度快。

2.deque构造函数

函数原型:

  • deque<T>//默认构造形式
  • deque(beg, end);//拷贝函数将[beg, end)区间的元素拷贝给自身
  • deque(n ,elem);//构造函数将n个elem拷贝给自身
  • deque(const deque &deq);//拷贝构造函数
void printDeque(const deque<int>&d){//打印不需要修改值,加const限制只读
    for(deque<int>::const_iterator it = d.begin();it != d.end();it ++){
        cout << *it << " ";
    }
    cout << endl;
}

int main(){
  	//第一种
    deque<int>d1;
    for (int i = 0; i < 10; ++i) {
        d1.push_back(i);
    }
    printDeque(d1);
		//第二种
    deque<int>d2(d1.begin(), d1.end());
    printDeque(d2);
		//第三种
    deque<int>d3(10,3);
    printDeque(d3);
		//第四种
    deque<int>d4(d3);
    printDeque(d4);
}

3.deque赋值操作

  • deque& operatot=(const deque &deq);//重载等号操作符
  • assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给自身
  • assign(n, elem);//将n个elem拷贝赋值给自身

4.deque大小操作

函数原型:

  • empty();//判断容器是否为空
  • size();//返回容器中元素的个数
  • resize(int num);//重新指定容器的长度为num,容器变长则以默认值0填充新位置,变短则删除超出部分
  • resize(int num, elem);//重新制定容器长度num,变长则以elem填充新位置,变短则删除超出部分

5.deque插入和删除

函数原型:

​ 两端插入操作:

  • push_back(elem);//尾部插入元素elem

  • push_front(elem);//头部插入元素elem

  • pop_back();//删除最后一个元素

  • pop_front();//删除第一个元素

    指定位置操作:

  • insert(pos, elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置

  • insert(pos, n, elem);//在pos位置插入n个元素elem,无返回值

  • insert(pos, beg, end);//在pos位置插入[begin, end)的数据,无返回值

  • clear();//删除容器中所有元素

  • erase(beg, end);//删除[beg, end)区间的数据,返回下一个数据的位置

  • erase(pos);//删除pos位置的数据,返回下一个数据的位置

6.deque数据存取

函数原型:

  • at(int idx);//返回索引idx所指的数据
  • operator[]//返回索引idx所指的数据,直接使用[]返回数据
  • front();//返回容器中第一个数据元素
  • back();//返回容器中最后一个数据元素

7.deque排序

sort(iterator beg, iterator end)//对beg和end区间内元素排序

排序默认升序排,对于支持随机访问的迭代的的容器,都可以用sort算法直接排序。

(四)stack容器

1.基本概念

stack:栈,是先进后出的容器,只有一个出口。只有顶端的元素可以使用,所以不允许有遍历行为。

2.常用接口

(1)构造函数:

  • stack<T> stk;//stack采用模板类实现,stack对象的默认构造形式
  • stack(const stack &stk);//拷贝构造函数

(2)赋值操作:

  • stack& operator=(const stack &stk);//重载等号操作符

(3)数据存取:

  • push(elem);//向栈顶添加元素
  • pop();//从栈顶移除第一个元素
  • top();//返回栈顶元素

(4)大小操作:

  • empty();//判断堆栈是否为空
  • size();//返回栈的大小

(五)queue容器

1.基本概念

先进先出的队列,从一端新增,另一端移除元素。队列中只有队头和队尾可以被使用,因此不允许有遍历行为。

2.常用接口

(1)构造函数:

  • queue<T> que;//queue采用模板类实现,queue对象的默认构造形式
  • queue(const queue &que);//拷贝构造函数

(2)赋值操作:

  • queue& operator=(const queue &que);//重载等号操作符

(3)数据存取:

  • push(elem);//向队尾添加元素
  • pop();//从队头移除第一个元素
  • back();//返回最后一个元素
  • front();//返回第一个元素

(4)大小操作:

  • empty();//判断队列是否为空
  • size();//返回队列的大小

(六)list容器

1.list基本概念

​ list(链表):将数据进行链式存储,由一系列结点组成,结点包括存储数据元素的数据域存储下一个结点地址的指针域组成。STL中的链表是双向循环链表

链表优缺点:

​ 可以快速插入和删除元素,并且删除和插入数据不会造成迭代器的失效。采用动态存储分配,不会内存浪费或溢出。但遍历速度没有数组快,且占用空间比数组大。

2.list构造函数

函数原型:

  • list<T> lst//默认构造形式
  • list(beg, end);//拷贝函数将[beg, end)区间的元素拷贝给自身
  • list(n ,elem);//构造函数将n个elem拷贝给自身
  • list(const list &lst);//拷贝构造函数
void printList(const list<int>&l){
    for(list<int>::const_iterator it = l.begin();it != l.end(); it ++){
        cout << *it << " ";
    }
    cout << endl;
}

int main(){
    //默认构造
    list<int>l1;
    l1.push_back(1);
    l1.push_back(3);
    l1.push_back(4);
    printList(l1);
    //区间方式构造
    list<int>l2(l1.begin(), l1.end());
    printList(l2);
    //拷贝构造
    list<int>l3(l2);
    printList(l3);
    //n个elem
    list<int>l4(10,20);
    printList(l4);
}

3.list赋值和交换

  • assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给自身
  • assign(n, elem);//将n个elem拷贝赋值给自身
  • list& operator=(const list &lst);//重载等号运算符
  • swap(lst);//将lst与本身的元素互换

4.list大小操作

函数原型:

  • empty();//判断容器是否为空
  • size();//返回容器中元素的个数
  • resize(int num);//重新指定容器的长度为num,容器变长则以默认值0填充新位置,变短则删除超出部分
  • resize(int num, elem);//重新制定容器长度num,变长则以elem填充新位置,变短则删除超出部分

5.list插入和删除

函数原型:

​ 两端操作:

  • push_back(elem);//尾部插入元素elem

  • push_front(elem);//头部插入元素elem

  • pop_back();//删除最后一个元素

  • pop_front();//删除第一个元素

    指定位置操作:

  • insert(pos, elem);//在pos位置(迭代器)插入一个elem元素的拷贝,返回新数据的位置

  • insert(pos, n, elem);//在pos位置插入n个元素elem,无返回值

  • insert(pos, beg, end);//在pos位置插入[begin, end)的数据,无返回值

  • clear();//删除容器中所有元素

  • erase(beg, end);//删除[beg, end)区间的数据,返回下一个数据的位置

  • erase(pos);//删除pos位置的数据,返回下一个数据的位置

  • remove(elem);//移除容器中所有与elem值匹配的元素

6.list数据存取

函数原型:

  • front();//返回第一个元素
  • back();//返回最后一个元素

7.list逆序和排序

  • reverse();//逆序链表
  • sort();//链表排序,所有不支持随机访问迭代器的容器,不能用标准算法,内部提供此sort算法

若降序排列,则:

bool compare(int v2, int v2){
	return v1 > v2;//降序排列,则让v1>v2
}
lst.sort(compare);

8.排序案例

class Persons{
public:
    Persons(string name, int age, int height){
        this->mName = name;
        this->mAge = age;
        this->mHeight = height;
    }
    string mName;
    int mAge;
    int mHeight;
};


void printPerson(list<Persons>&p){
    for(list<Persons>::iterator it = p.begin();it != p.end(); it ++){
        cout << "姓名:" << it->mName << " 年龄:" << it->mAge << " 身高:" << it->mHeight << endl;
    }
}

//指定排序规则,按年龄升序,年龄相同时按照身高降序排列
bool compare(Persons &p1, Persons &p2) {
    if (p1.mAge == p2.mAge) {
        return p1.mHeight > p2.mHeight;
    } else {
        return p1.mAge < p2.mAge;
    }
}

int main(){
    list<Persons>l;
    Persons p1("小花", 23, 171);
    Persons p2("小蓝", 12, 155);
    Persons p3("小紫", 12, 165);
    Persons p4("小绿", 34, 174);
    Persons p5("小黑", 12, 130);
    l.push_back(p1);
    l.push_back(p2);
    l.push_back(p3);
    l.push_back(p4);
    l.push_back(p5);

    //打印数据
    cout << "————————排序前:————————" << endl;
    printPerson(l);
    //按照年龄进行升序排列
    l.sort(compare);
    cout << "————————排序后:————————" << endl;
    printPerson(l);
}

(七)set/multiset容器

1.基本概念

所有元素在插入时自动排序,属于关联式容器,底层结构使用二叉树实现。

set和multiset区别:

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

2.set构造函数和赋值

构造:

  • set<T> st;//默认构造函数
  • set<const set &st>;//拷贝构造函数

赋值:

  • set& operator=(const set &st);//重载等号操作符

3.set大小和交换

函数原型:

  • size();//返回容器中元素的数目
  • empty();//判断容器是否为空
  • swap(st);//交换两个集合容器

4.set插入和删除

函数原型:

  • insert(elem);//在容器中插入元素
  • clear();//清空容器元素
  • erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
  • erase(elem);//删除容器中值为elem的元素

5.set查找和统计

函数原型:

  • find(key);//查找key是否存在,存在返回该元素的迭代器,不存在返回set.end()
  • count(key);//统计key的元素个数:对set只能是0或1

6.set和multiset的区别

  • set不能插入重复数据,multiset可以

  • set插入数据的同时会返回插入结果,使用对组pair:

  •   pair<set<int>::iterator, bool> ret = st.insert(1);
      if(ret.second){
        cout << "插入成功!"; 
      }
    
  • multiset不会检测数据,可以重复插入数据

7.pair对组创建

成对出现的数据,利用对组可以返回两个数据。有两种创建方式:

  • pair<type, type> p (value1, value2);
  • pair<type, type> p = make_pair(value1, value2);
//第一种方式
    pair<string, int>p("wzh", 25);
    //第二种方式
    pair<string, int>p2 = make_pair("wzh", 24);
    cout << p.first << p.second;

8.set容器排序

​ set容器默认从小到大排序,利用仿函数可以改变排序规则。自带数据类型可以不指定排序规则,自定数据类型必须指定。

(1)自带数据类型:

//使用仿函数指定规则
class Compares{
public:
    bool operator()(int v1, int v2){
        return v1 > v2;
    }
};
int main(){
    set<int>s1;
    s1.insert(6);
    s1.insert(1);
    s1.insert(3);
    s1.insert(2);
    s1.insert(4);

    for(set<int>::iterator it = s1.begin();it != s1.end();it ++){
        cout << *it << " ";
    }
    
    //指定排序规则为降序
    set<int, Compares>s2;//指定仿函数
    s2.insert(6);
    s2.insert(1);
    s2.insert(3);
    s2.insert(2);
    s2.insert(4);

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

(2)自定义数据类型:

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

class SetCompare{
public:
    bool operator()(const Person&p1, const Person&p2){
        return p1.mAge > p2.mAge;
    }
};

int main(){
    //————————————自定义数据类型————————————
    set<Person, SetCompare>s;
    Person p1("大B",20);
    Person p2("小C",12);
    Person p3("大A",25);
    Person p4("小D",11);
    //s.insert(p1);自定义数据类型不能按照默认升序排,需要自定仿函数排序规则
    s.insert(p1);
    s.insert(p2);
    s.insert(p3);
    s.insert(p4);
    //输出
    for(set<Person>::iterator it = s.begin();it != s.end();it ++){
        cout << "姓名:" << it->mName << " 年龄:" << it->mAge << endl;
    }
}

(八)map/multimap容器

1.基本概念

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

map/multimap属于关联式容器,底层结构使用二叉树实现。map不允许有重复值,multimap允许有重复值。

2.map构造和赋值

构造:

  • map<T1, T2>mp;//map默认构造函数
  • map(const map &mp);//拷贝构造函数

赋值:

  • map& operator=(const map &mp);//重载等号操作符
map<int, int>mp;
    mp.insert(pair<int ,int>(5, 14));
    mp.insert(pair<int ,int>(3, 10));
    mp.insert(pair<int ,int>(7, 8));
    
		mp.insert(make_pair(4, 20));
    for(map<int, int>::iterator it = mp.begin();it != mp.end();it ++){
        cout << "key:" << (*it).first << " value:" << it->second << endl;
    }

3.map大小和交换

函数原型:

  • size();//返回容器中元素的数目
  • empty();//判断容器是否为空
  • swap(st);//交换两个集合容器

4.map插入和删除

函数原型:

  • insert(elem);//在容器中插入元素
  • clear();//清空容器元素
  • erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
  • erase(beg, end);//删除区间的所有元素,返回下一个元素的迭代器
  • erase(key);//删除容器中键值为key的键值对

可以用key[xx]的方式获取键值对。

5.map查找和统计

函数原型:

  • find(key);//查找key是否存在,存在返回该键的元素的迭代器,不存在返回map.end()
  • count(key);//统计key的元素个数:对map只能是0或1

6.map容器排序

​ map容器默认排序规则是按key值升序排列,利用仿函数可以改变排序规则,对于自定义数据类型,map必须制定排序规则:

class compare{
public:
  	bool operator()(int v1, int v2){
      	return v1 > v2;//降序
    }
}

7.员工分组信息显示案例:

class Worker{
public:
    string mName;
    int mSalary;
};
//创建员工
void create(vector<Worker>&v){
    string nameSeed = "ABCDEFGHIJ";
    for (int i = 0; i < 10; ++i) {
        Worker worker;
        worker.mName = "员工";
        worker.mName += nameSeed[i];
        worker.mSalary = rand()%10000 + 10000;//10000~19999
        v.push_back(worker);
    }
}
//员工分组
void group(vector<Worker>&v, multimap<int, Worker>&mp){
    for(vector<Worker>::iterator it = v.begin();it != v.end();it ++){
        //产生随机部门编号
        int depId = rand()%3;//0 1 2
        //将员工插入到分组中
        mp.insert(make_pair(depId, *it));
    }
}
//打印员工姓名和工资
void printVector(vector<Worker>&v){
    for(vector<Worker>::iterator it = v.begin();it != v.end();it ++){
        cout << "姓名:" << it->mName << " 工资:" << it->mSalary << endl;
    }
}
//分组打印员工信息
void printWorkerOfGroup(multimap<int, Worker>&mp){
    multimap<int, Worker>::iterator it;
    for (int i = 0; i < 3; ++i) {//循环打印三个部门的信息
        cout << "部门" << i << ":" << endl;
        it  = mp.find(i);
        int count = mp.count(i);
        int index = 0;
        for(;it != mp.end() && index < count;it ++, index ++){
            cout << "部门:" << it->first << " 姓名:" << it->second.mName
                 << " 工资:" << it->second.mSalary <<endl;
        }
    }
}
int main(){
    vector<Worker>v;
    create(v);//创建员工
    printVector(v);//输出员工信息
    cout << "-----------------------" << endl;
    multimap<int, Worker>mp;
    group(v, mp);//员工分组
    printWorkerOfGroup(mp);//分组显示员工的部门、姓名、工资
}

四、函数对象

(一)函数对象(仿函数)

1.函数对象概念

  • 重载函数调用操作符的类,其对象称为函数对象

  • 函数对象使用重载的()时,行为类似函数调用,也叫仿函数

  • 函数对象(仿函数)是一个类,不是函数

2.函数对象使用

特点:

  • 函数对象在使用时,可以像普通函数那样调用,可以有参有返回值
  • 函数对象超出普通函数的概念,函数对象内部可以有自己的状态(成员属性等)
  • 函数对象可以作为参数传递
class MyAdd{
public:
    //1.使用时像普通函数,可有参 有返回
    int operator()(int v1, int v2){
        return v1 + v2;
    }
};
//2.函数对象内部可以有自己的状态(成员属性)
class MyPrint{
public:
    MyPrint(){
        this->count = 0;
    }
    void operator()(string test){
        cout << test << endl;
        this->count ++;
    }
    int count;//内部自己的状态,记录函数被调用次数
};
//3.函数对象可以作为参数传递
void doPrint(MyPrint &mp, string test){
    mp(test);
}

int main(){
    MyAdd myAdd;//1.这是函数对象
    cout << myAdd(1, 5);
    //2.打印调用次数
    MyPrint myPrint;
    myPrint("yeah!");
    myPrint("yeah!");
    myPrint("yeah!");
    myPrint("yeah!");
    cout << myPrint.count << endl;
    //3.作为对象传递
    MyPrint myPrint1;
    doPrint(myPrint1,"hello~~");

}

(二)谓词

1.谓词概念

  • 返回bool类型的仿函数称为谓词
  • 如果operator()接收一个参数为一元谓词,接收2个参数为二元谓词

2.谓词示例

class FiveBig{//仿函数,查找大于5的数
public:
    bool operator()(int val){//bool类型,一个参数
        return val > 5;
    }
};

class Compare{
public:
    bool operator()(int v1, int v2){
        return v1 > v2;
    }
};

int main(){
    vector<int>v;
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);
    }
    //创建匿名对象
    vector<int>::iterator it = find_if(v.begin(), v.end(),FiveBig());
    if (it == v.end()){
        cout << "未找到" << endl;
    }else{
        cout << "找到:" << *it << endl;
    }

    vector<int>v2;
    v2.push_back(4);
    v2.push_back(1);
    v2.push_back(3);
    sort(v2.begin(), v2.end(), Compare());
    for(vector<int>::iterator it = v2.begin();it != v2.end();it++){
        cout << *it << " ";
    }
}

(三)内建函数对象

1.意义

STL内置了一些函数对象,包括算术仿函数、关系仿函数、逻辑仿函数,这些仿函数所产生的对象,用法和一般函数完全相同,使用时引入头文件functional

2.算数仿函数

实现size运算,negate是一元运算,其他为二元。

仿函数原型:

  • template<class T> T plus<T>//加法仿函数
  • template<class T> T minus<T>//减法仿函数
  • template<class T> T multiplies<T>//乘法仿函数
  • template<class T> T divides<T>//除法仿函数
  • template<class T> T modulus<T>//取模仿函数
  • template<class T> T negate<T>//取反仿函数
//negate一元仿函数
negate<int>n;
int ret = n(3);
cout << ret << endl;//输出-3

//加法 二元仿函数
plus<int>p;//只允许两个相同类型
cout << p(4,6) << endl;

3.关系仿函数

仿函数原型:

  • template<class T> bool equal_to<T>//等于
  • template<class T> bool not_equal_to<T>//不等于
  • template<class T> bool greater<T>//大于
  • template<class T> bool greater_equal<T>//大于等于
  • template<class T> bool less<T>//小于
  • template<class T> bool less_equal<T>//小于等于
int main(){
    vector<int>v;
    v.push_back(5);
    v.push_back(2);
    v.push_back(1);
    //使用关系仿函数greater 降序排列
    sort(v.begin(), v.end(), greater<int>());
}

4.逻辑仿函数(用途少)

  • template<class T> bool logica_and<T>//逻辑与
  • template<class T> bool logical_or<T>//逻辑或
  • template<class T> bool logical_not<T>//逻辑非
vector<bool>v1;
v1.push_back(true);
v1.push_back(false);
//将v1中元素取反后到v2中
vector<bool>v2;
v2.resize(v1.size());//先开辟空间,再搬运
transform(v1.begin(), v1.end(), v2.begin(),logical_not<bool>());

五、STL常用算法

概述:

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

(一)常用遍历算法

  • for_each();//遍历容器
  • transform();//搬运容器到另一个容器中

1.for_each算法(常用)

函数原型:

for_each(iterator beg, iterator end, _func);//开始/结束迭代器beg/end,_func函数或函数对象

//普通函数,打印输出
void printVec(int val){
    cout << val << " ";
}
//仿函数,打印输出
class PrintFunc{
public:
    void operator()(int val){
        cout << val << " ";
    }
};
int main(){
    vector<int>v;
    v.push_back(2);
    v.push_back(1);
    v.push_back(3);
    for_each(v.begin(), v.end(), printVec);//普通函数
    for_each(v.begin(), v.end(), PrintFunc());//仿函数
}

2.transform算法

​ 搬运容器到另一个容器中。

函数原型:

transform(iterator beg1, iterator end1,iterator beg2, _func);

//原容器开始和结束迭代器;目标容器开始迭代器;_func函数或函数对象

class Transform{
public:
    int operator()(int val){
        return val + 10;//搬运过程中可以进行运算
    }
};
class PrintFun{
public:
    void operator()(int val){
        cout << val << " ";
    }
};
int main(){
    vector<int>v;
    v.push_back(3);
    v.push_back(1);
    v.push_back(2);
    vector<int>vTarget;
    vTarget.resize(v.size());//搬运前提前开辟空间
    transform(v.begin(), v.end(), vTarget.begin(),Transform());
    for_each(vTarget.begin(), vTarget.end(), PrintFun());
}

(二)常用查找算法

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

1.find算法

​ 查找指定元素,找到返回指定元素的迭代器,找不到返结束迭代器。

函数原型:

find(iterator beg, iterator end, value);

//beg/end:开始/结束迭代器,value查找的元素

示例:查找内置数据类型和自定义数据类型

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    //重载==,底层find函数才知道如何对比Person类型
    bool operator==(const Person &p){
        if(this->mName == p.mName && this->mAge == p.mAge){
            return true;
        } else{
            return false;
        }
    }
    string mName;
    int mAge;
};

int main(){
    //——————————查找内置数据类型————————————
    vector<int>v;
    v.push_back(5);
    v.push_back(1);
    v.push_back(3);

    vector<int>::iterator it = find(v.begin(), v.end(), 5);
    if(it == v.end()){
        cout << "没找到"<< endl;
    } else{
        cout << "找到:" << *it << endl;
    }

    //——————————查找自定义数据类型————————————
    vector<Person>vPerson;
    Person p1("小李", 20);
    Person p2("小刘", 14);
    Person p3("小孙", 34);
    vPerson.push_back(p1);
    vPerson.push_back(p2);
    vPerson.push_back(p3);
    //下面的find需要重载==运算符
    vector<Person>::iterator its = find(vPerson.begin(), vPerson.end(), p2);
    if(it == v.end()){
        cout << "没找到!" << endl;
    } else{
        cout << "找到:" << its->mName << its->mAge << endl;
    }
}

2.find_if算法

​ 按条件查找元素,找到返回指定元素的迭代器,找不到返结束迭代器。

函数原型:

find_if(iterator beg, iterator end, _Pred);

//beg/end:开始/结束迭代器,_Pred:函数或者谓词(返回bool类型的仿函数)

示例:条件查找内置数据类型和自定义数据类型

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    string mName;
    int mAge;
};
class GreaterFive{//谓词,大于5
public:
    bool operator()(int val){
        return val > 5;
    }
};
class AgeGreater20{//谓词,年龄大于20
public:
    bool operator()(const Person &p){
        return p.mAge >20;
    }
};

int main(){
    //——————————条件查找 内置数据类型————————————
    vector<int>v1;
    v1.push_back(5);
    v1.push_back(6);
    v1.push_back(3);

    vector<int>::iterator it1 = find_if(v1.begin(), v1.end(),GreaterFive());
    if(it1 == v1.end()){
        cout << "没找到"<< endl;
    } else{
        cout << "找到大于5的数:" << *it1 << endl;
    }

    //——————————条件查找 自定义数据类型————————————
    vector<Person>v2;
    Person p1("小李", 20);
    Person p2("小刘", 14);
    Person p3("小孙", 34);
    v2.push_back(p1);
    v2.push_back(p2);
    v2.push_back(p3);

    vector<Person>::iterator it2 = find_if(v2.begin(), v2.end(), AgeGreater20());
    if(it2 == v2.end()){
        cout << "没找到!" << endl;
    } else{
        cout << "找到:" << it2->mName << it2->mAge << endl;
    }
}

3.adjacent_find算法

​ 查找相邻重复元素,返回相邻元素的第一个位置的迭代器。

函数原型:

adjacent_find(iterator beg, iterator end);

//beg/end:开始/结束迭代器

int main(){
    //——————————条件查找 内置数据类型————————————
    vector<int>v;
    v.push_back(0);
    v.push_back(3);
		v.push_back(3);
    v.push_back(1);
    v.push_back(3);
    vector<int>::iterator pos = adjacent_find(v.begin(),v.end());
    if (pos == v.end()){
        cout << "未找到相邻重复元素!" << endl;
    } else{
        cout << "找到相邻重复元素:" << *pos << endl;
    }
}

4.binary_search算法

​ 二分查找法,查找指定元素是否存在,存在返回true,不存在返回false。在无序序列中不可用,无序序列中查找结果未知。

函数原型:

bool binary_search(iterator beg, iterator end,value);

//beg/end:开始/结束迭代器,value:查找的元素

int main() {
    //——————————二分查找,容器为有序序列————————————
    vector<int> v;
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);
    }
    bool ret = binary_search(v.begin(), v.end(),9);
    if (ret) {
        cout << "找到元素!" << endl;
    } else {
        cout << "未找到相邻重复元素!" << endl;
    }
}

5.count算法

​ 统计元素个数。

函数原型:

count(iterator beg, iterator end, value);

示例:统计内置数据类型和自定义数据类型

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    //重载==,底层count函数才知道如何对比Person类型
    bool operator==(const Person &p){
        if(this->mAge == p.mAge){
            return true;
        } else{
            return false;
        }
    }
    string mName;
    int mAge;
};
int main() {
    //——————————1.查找内置数据类型————————————
    vector<int> v;
    v.push_back(2);
    v.push_back(3);
    v.push_back(1);
    v.push_back(3);
    int num1 = count(v.begin(), v.end(),3);
    cout << num1 << endl;

    //——————————2.查找自定义数据类型————————————
    vector<Person>v2;
    Person p1("二哥", 20);
    Person p2("三哥", 14);
    Person p3("大哥", 34);
    Person p("大A",14);//用于检测
    v2.push_back(p1);
    v2.push_back(p2);
    v2.push_back(p3);
    int num2 = count(v2.begin(), v2.end(), p);
    cout << "和大A同岁的人个数" << num2 << endl;
}

6.count_if算法

​ 按条件统计元素个数。

函数原型:

count_if(iterator beg, iterator end, _Pred);

//_Pred:谓词

示例:条件统计内置数据类型和自定义数据类型的个数

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    string mName;
    int mAge;
};
class GreaterTen{//谓词,大于10
public:
    bool operator()(int val){
        return val > 10;
    }
};
class AgeGreater20{//谓词,年龄大于20
public:
    bool operator()(const Person &p){
        return p.mAge >20;
    }
};

int main(){
    //——————————条件统计 内置数据类型————————————
    vector<int>v1;
    v1.push_back(9);
    v1.push_back(20);
    v1.push_back(22);
    v1.push_back(11);
    int num1 = count_if(v1.begin(), v1.end(),GreaterTen());
        cout << "大于10的个数:"<< num1 << endl;

    //——————————条件统计 自定义数据类型————————————
    vector<Person>v2;
    Person p1("小马", 12);
    Person p2("小猪", 43);
    Person p3("小牛", 34);
    v2.push_back(p1);
    v2.push_back(p2);
    v2.push_back(p3);

    int num2 = count_if(v2.begin(), v2.end(), AgeGreater20());
    cout << "大于20岁的人员个数:"<< num2 << endl;

}

(三)常用排序算法

  • sort();//对容器内元素进行排序
  • random_shuffle();//洗牌,指定范围内元素随机调整次序
  • merge();//容器元素合并,并存储到另一容器中
  • reverse();// 反转指定范围内的元素

1.sort算法

函数原型:

sort(iterator beg, iterator end, _Pred);

//_Pred谓词,可填可不填

2.random_shuffle算法

函数原型:

random_shuffle(iterator beg, iterator end);

3.merge算法

函数原型:

merge(iterator beg1, iterator end1, iterator beg2, iterator end2,iterator dest);

//dest:目标容器开始迭代器

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

int main() {
    //——————————merge合并两个容器到另一个容器,必须为有序序列————————————
    vector<int> v1;
    vector<int> v2;
    vector<int> vTarget;//目标容器
    for (int i = 0; i < 10; ++i) {
        v1.push_back(i);
        v2.push_back(i+2);
    }
    //先给目标容器开辟空间
    vTarget.resize(v1.size() + v2.size());

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

}

4.reverse算法

函数原型:

reverse(iterator beg, iterator end);```

(四)常用拷贝替换算法

  • copy();//容器中指定范围的元素拷贝到另一容器中
  • replace();//容器中指定范围的旧元素更改为新元素
  • replace_if();//容器中指定范围的满足条件的元素替换为新元素
  • swap();//互换两个容器中的元素

1.copy算法

函数原型:

copy(iterator beg, iterator end, iterator dest);//dest:目标起始迭代器,目标容器提前开辟空间

2.replace算法

函数原型:

replace(iterator beg, iterator end, oldvalue, newvalue);

3.replace_if算法

函数原型:

replace_if(iterator beg, iterator end, _Pred, newvalue);

void printVect(int val){
    cout << val << " ";
}
class GreaterFive{//谓词,大于5
public:
    bool operator()(int val){
        return val > 5;
    }
};

int main(){
    vector<int>v;
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);
    }
    replace_if(v.begin(), v.end(),GreaterFive(),100);
    for_each(v.begin(), v.end(), printVect);
}

4.swap算法

swap(container c1, container c2);//交换同类型的容器

(五)常用算数生成算法

算数生成算法是小型算法,在头文件<numeric>

  • accumulate();//计算容器元素累计总和,返回int
  • fill();//向容器中添加元素

1.accumulate算法

accumulate(iterator beg, iterator end, value);//value:起始累加值

2.fill算法

fill(iterator beg, iterator end, value);//value:填充值

(六)常用集合算法

  • set_intersection();//求两个容器的交集,放到另一个容器中
  • set_union();//求两个容器的并集
  • set_difference();//求两个容器的差集

三个函数的返回值是:新容器的最后位置的迭代器。2个原容器必须是有序序列。

1.set_intersection算法

set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

目标容器开辟空间为小容器size()。

2.set_union算法

set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

目标容器开辟空间为两容器和。

3.set_difference算法

set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

目标容器开辟空间为大容器size()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值