• 一幅长文细学算法(一)——C++STL


    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • C++中用于输入输出的头文件为iostream,类似于C中大的stdio.h。在大部分编译器中会包含文件。
    • C语言的头文件在C++里全部可以使用,但是要把.h后缀去掉,在头文件前面加上c。
    • C++中,用标准库的东西前面都要加上std::。但是如果加上using namespace std导入命名空间,则下面的代码无需加上std::,虽然这样便捷,但起名的时候容易和库函数起冲突。
    • cout是C++中用于输出的对象,类似于C中的printf,所有标准数据类型都可以用cout输出。
    • endl等于回车"\n",但有些细节上的不同。这样写的可读性更好一点。

    1.1.2 输出

    #include 
    using namespace std;
    int main(){
    	int a = 1;
    	string b = "hello";
    	cout << a << endl;
    	cout << b << endl;
    	cin.get();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • C++中输出使用cin,相当于C中的scanf,所有标准数据类型都可以用cin输入。

    • >> 和<<代表输入流的方向。

    • cin.getline(读取字符串,读取字符数)用于读取一行,但要加入读取的字符数上限。


    1.1.3 输入输出流的使用细节

    • 如果不是打ACM,则建议使用C++提供的输入输出。
    • cin的速度比scanf慢不少,1e5以上的数据用cin读入就可能会TLE,此时建议用scanf。
    • 输出小数用printf更方便一点,C++格式输出要用到头文件,用cout.setprecistion(int digit)来修改精度。
    • 使用cin可以快速解决EOF问题,EOF是文件结束符的意思,我们都知道字符串本质是字符数组,如果使用scanf是没有自动结束功能的,但是cin可以帮我们做到这件事。

    1.2 语法特性

    1.2.1 新增的布尔类型

    • C语言是没有布尔类型的,C++添加了新的基本数据类型bool,有true和false两个值。
    • 逻辑中用true来代替非0,false来代替0能有效提高程序可读性。

    1.2.2 动态开辟内存

    int* number = new int;
    int* arr = new int[100];
    int* carr = (int*)malloc(100*sizeof(int));
    
    • 1
    • 2
    • 3
    • 与C中的malloc类似,C++中用new来动态开辟内存,new写起来更加简明一些。
    • C++不支持变长数组。
    • 平常写题的代码可以不用释放内存,但是自己手动释放是一个非常好的习惯。

    1.2.3 引用

    void swapInt(int&a, int&b){
    	int c = a;
    	a = b;
    	b = c;
    }
    
    int main(){
    	int a = 1,b =2;
    	swapInt(a,b);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • C++中使用&来创建引用。可以把引用当成一个不能改变指向对象的指针,或者你可以说他是地址的别名。
    • 引用多数情况在函数传参的时候才会用到,以简化指针的代码。

    1.2.4 函数重载

    int add(int a, int b){
    	return a+b;
    }
    int add(int a){
    	return a;
    }
    int minus(int a,int b = 0){
    	return a-b;
    }
    
    int main(){
    	add(0);
    	add(0,0);
    	minus(0);
    	minus(0,0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • C++中,函数是以函数名+参数列表来区分的,也就是说,两个函数可以名字相同,但参数列表和返回值不同。
    • 函数的部分参数可以缺省,没有提供参数时用缺省值代替。
    • 函数重载在dfs使用非常方便。

    1.2.5 struct

    struct node{
    	int number;
    	node* next;
    };
    //struct node* head;
    node* head;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 结构不用再和C语言一样在前面加一个struct,可以直接使用结构的名字。

    struct node{
    	int number;
    	node* next;
    	node(int _number = 0,node* _next = nullptr){
    		number = _number;
    		next = _next;
    	}
    };
    
    int main(){
    	node a = node(0);
        node* b = new node(1,&a);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • struct中可以加入与结构同名,无返回值的构造函数。在创建struct的时候会自动调用构造函数。
    • 构造函数与缺省参数配合使用会让代码更简洁。

    1.3 C++标准库

    1.3.1 C语言的标准库

    函数说明
    strlen()字符串长度
    strcmp()字符串比较
    strcpy()字符串拷贝
    memset()暴力清空
    memcpy()暴力拷贝

    三角函数、指数函数、浮点取整函数


    函数说明
    qsort()C语言快排,调用复杂一般不用而使用C++的排序
    rand()随机数
    malloc()申请内存
    free()释放内存

    函数说明
    time(0)从1970年到现在的描述,常配合随机数用于初始化
    clock()程序启动到目前为止的毫秒数

    函数说明
    isdigit()判断字符是否为数字
    isalpha()判断大小写字母

    1.3.2 C++STL容器

    1.3.2.1 vector向量

    vector简介

    vector arr1(100);
    vector arr2;
    vector arr3 = {1,2,3,4,5};
    vector arr4{1,2,3,4,5};
    int arr2[100];
    
    vector list;
    list.push_back(1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • vector可以看做一个超级数组,但它本质是个容器。
    • vector即可以和C语言的数组一样用下标访问,也可以像链表一样动态改变长度。
    • push_back()可以往vector的尾部塞入元素。
    • size()可以返回vector的长度。

    vector的遍历

    • 通过for循环遍历。
    • 通过增强for循环遍历。增强for循环的语法为for(遍历:容器){语句}。

    迭代器

    vector arr1(100);
    int arr2[100];
    
    vector::iterator p1;
    int* p2;
    
    for(p1 = arr1.begin();p1 != arr1.end();p1++){
    	cout << *p1<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    与普通的数组类似,vector也可以使用指针来访问遍历每一个元素。STL的指针被称为迭代器

    C++的迭代器需要指明类型,如需要访问整型向量的迭代器写作vector::iterator p1;

    begin()是首元素迭代器,begin默认在容器的首元素的地址。end同理,默认在容器的尾元素的下一个地址


    vector基本操作

    声明:vector list;

    函数说明
    list.size()数组元素个数,复杂度O(1)
    list.clear()一键清空数组,而非删除数组,复杂度O(n)
    list.empty()判空,复杂度O(1)
    list.begin()首元素迭代器,复杂度O(1)
    list.end()尾元素的下一位元素的迭代器,复杂度O(1)
    list.erase()删除数组某个迭代器所在位置的数字,复杂度O(n)
    list.push_back()往数组后添加元素,复杂度O(1)
    list.pop_back()删除数组最后一个元素,复杂度O(1)

    1.3.2.2 string字符串

    string简介

    string str1 = "hello";
    char str2[] = "world";
    string str3;
    
    str3.push_back('!');
    
    cout << str1 << " " << str2 << str3 << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 字符串string可以看成一个特殊的vector。
    • string和c语言字符串的关系就和vector和普通数组的关系一样。
    • C++中的字符串可以很方便的拼接。
    • vector有的操作string基本都有,唯一的区别就是size的复杂度。
    • 所有参数为字符串的地方既可以是string也可以是C字符串。

    string基本操作

    声明:string str = “hello”

    函数说明
    str.length()等同于str.size(),复杂度O(n)
    str.insert(1,“a”)在下标为1处插入字符或字符串,复杂度O(1)
    str.insert(str.begin,‘a’)在迭代器处插入一个字符,复杂度O(n)
    str.c_str()返回C语言风格的字符串,复杂度O(n)
    str.append(str2)把str2拼接到str后面,复杂度O(n)
    str.compare(str2)比较字符串
    str += str2相当于拼接
    str += ‘a’相当于push_back

    1.3.2.3 stack栈
    stack sta;
    sta.push(1);
    int topElement = sta.top();
    sta.pop();
    sta.empty();
    sta.size();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • STL大部分数据结构和vector一样,都会自带size()和empty()函数
    • stack操作包括入、出、获取栈顶元素,复杂度都是O(1)

    1.3.2.4 queue队列
    queue que;
    que.push(1);
    int frontElement = que.front();
    que.pop();
    que.empty();
    que.size();
    
    priority_queue que2;
    que2.push(1);
    int minElement = que2.top();
    que2.pop();
    que2.empty();
    que.size();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.3.2.5 set集合
    set st;
    st.insert(1);
    st.find(1);
    st.erase(1);
    
    multiset mst;
    mst.insert(1);
    mst.insert(1);
    mst.count(1); //2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • set用于保存很多很多元素,能够在O(logn)的时间内查找、删除、添加某个元素
    • 迭代器中的++和–能够在O(logn)的时间里找到第一个比它大(小)的数
    • set自带去重,而multiset允许元素重复,通过count可以获得某个元素的数量
    • set是用红黑平衡树实现的

    1.3.2.6 map 映射
    pair origin;
    origin = make_mair(0,0);
    origin.first == origin.second;
    origin.swap;
    
    pairid;
    id = make_pair("somebody",110);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 包含了数据结构pair数对。它可以由任意两种类型构成。
    • 如果我们不像以pairid这样的方式写出数据结构,则可以使用make_pair方法来构造一个数对并返回。
    • pair自带了比较函数,默认先比第一个元素再比较第二个。

    pair id;
    id = make_pair("somebody",110);
    
    map studentHeight;
    studentHeight["小明"] = 170;
    studentHeight["小红"] = 150;
    studentHeight.insert(id);
    studentHeight.erase("小明");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 的第二个数据结构是map映射
    • map可以看成一个超级数组,我们可以把字符串或者其他类型当成数组的下标
    • 插入、查询、删除操作复杂度都是O(logn)
    • map内部使用了pair,所以我们也可以通过insert一个pair来插入
    • map可以使用大括号加大括号的方式初始化。

    1.3.2.7 bitset 位集合
    1.3.2.8 multiset哈希集合和multimap哈希表

    分别是的另外一种实现版本,前者使用哈希函数,可以达到O(1)的随机存取,后者使用的是红黑二叉搜索树,存取速度为O(logn)。

    有些毒瘤题目会卡map的时间,用unordered_map


    1.3.3 Algorithm算法函数

    algorithm和之前两个头文件不同,它没有定义什么新的类型,而是定义了很多算法,极大简化了代码量。


    1.3.3.1 sort快排

    sort的基本使用

    语法:sort(数组首元素地址,数位尾元素的下一位地址)

    int arr[] {2,3,1,5,4};
    int n = 5;
    
    sort(arr,arr+n);
    for(int i = 0;i arr;
    arr.push_back(2);
    arr.push_back(3);
    arr.push_back(1);
    sort(arr.begin(),arr.end());
    
    for(int var : arr){
    	cout << var << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    :C++sort排序的复杂度是O(nlogn)


    降序排列

    bool cmpInt(int a, int b){
    	return a>b;
    }
    vector arr;
    arr.push_back(2);
    arr.push_back(3);
    arr.push_back(1);
    sort(arr.begin(),arr.end(),cmpInt);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 和C语言的qsort一样,sort可以使用自定义的比较函数。比较函数的参数时两个待比较的变量,返回值是比较的bool值。
    • 内部排序函数是按小于关系来的,排序结果是升序。如果像上述代码一样按大于关系比较,则可以得到降序的排序结果。

    结构体的排序

    struct Point{
    	int x,y;
    };
    Point points[1111];
    
    bool cmp(Point a,Point b){
    	if(a.x != b.x){
    		return a.x

1.3.3.2 其他的算法函数
函数说明
max(1,2)返回最大值,复杂度为O(1)
min(1,2)返回最小值,复杂度为O(1)
min_element(arr.begin(),arr.end())数组最大指针,复杂度为O(n)
max_element(arr.begin(),arr.end())数组最小指针,复杂度为O(n)
nth_element(arr.begin(),arr.begin()+n,arr.end())查看排好序后的第n个位置里面的数据,复杂度为O(n)
swap(arr[0],arr[1])用于交换两个数据,复杂度为O(1)
reverse(arr.begin(),arr.end())用于反转数组,复杂度为O(n)
unique(arr.begin(),arr.end())大多数用于离散化的情况,能使得数组的前面部分变得独一无二,重复的数据放于数组的后面部分。复杂度为O(n)
binary_search(arr.begin(),arr.end(),1)在排好序的数组中进行二分查找,搜索对应元素是否存在,返回布尔值。时间复杂度为O(logn)
lower_bound(arr.begin(),arr.end(),2)在排好序的数组中返回第一个大于等于你所给定的值的位置。时间复杂度为O(logn)
upper_bound(arr.begin(),arr.end(),2)在排好序的数组中返回第一个大于你所给定的值的位置。时间复杂度为O(logn)

1.4 注意事项须知

1.4.1 打比赛的技巧

如果不想记忆C++的所有库函数,我们可以使用以下语句:

#include 
  • 1

它可以帮我们包含所有的头文件,Visual Studio用不了。


1.4.2 学习STL的正确姿势

使用Reference - C++ Reference (cplusplus.com)查看STL的正确用法。

使用编译器自动补全自己摸索细节。


1.4.3 其他算法竞赛的细节

  1. 1s时限内能做的运算次数大约为1e8,根据复杂度来算是否会超时
  2. C++输出double时不能用%lf,要用%f,不然会WA到哭
  3. 注意多组用例时的EOF、初始化。初始化的常数时间也得估计好
  4. 数据范围及运算结果均在1e9以内时,可以令const int INF = 0x3f3f3f3f表示无穷大值,并且可以用memset(arr,INF,sizeof(arr))来令数组初始化为无穷大。
  5. 在使用循环时请尽量使用++i,而非i++,这样的速度会快一点点,有时候有些题就卡这么点时间。
  6. 如果有一个函数经常被使用到,尽量将其保存为全局变量来使用而非经常去调用它,否则会比较耗时。

1.4.4 算法竞赛中常见的提示

  • 相关阅读:
    Centos/Ubuntu安装redis
    C#运算符和流程控制语句
    Selenium自动化测试-设置元素等待
    ErrCode: 13102001没有找到名称为 ‘xxx‘ 的数据源
    ZYNQ从vitis生成linux系统编译启动文件
    Nacos的使用记录
    51单片机控制电动机正反转,PWM调速,记录转动圈数。
    git clone失败
    2023年中国BaaS行业发展概况及未来发展趋势分析:未来多链支持和发展将是BaaS平台发展重点方向[图]
    AWS亚马逊服务器搭建VPN
  • 原文地址:https://blog.csdn.net/chengyuhaomei520/article/details/126606400