C++ STL 备战比赛整理资料

读入数据

//在数据量足够大的时候,防止手动输入的麻烦,我可以用下面的操作
#include<cstdio>
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);

如果大家想知道为什么这个可以实现就看看下面的这个文章:
freopen(“in.text”, “r”, stdin)的用法

C++ 和STL

如果想取得一个好成绩,一个好的编程语言是必不可少的,在这里我们就用C++,其中非常重要的就是STL标准模板库了,在这里我整理一下比较常用的string、容器、经典算法的资料。

一、string

为什么要用string呢,因为他的操作确实比字符串更简单

1.字符串拼接

有了 string 类,我们可以使用+或+=运算符来直接拼接字符串,非常方便,再也不需要使用C语言中的 strcat()、strcpy()、malloc() 等函数来拼接字符串了,再也不用担心空间不够会溢出了。

//字符串拼接
   string s1 = "abcde";
   string s2 = "fgh";
   s1 = s1 + s2;

输出:abcdefg

2… 插入字符串

函数原型:string& insert (size_t pos, const string& str);
注意:第一个参数越界会出现异常

//字符串插入
    string s1 = "abcde";
	string s2 = "fgh";
	s1.insert(2, s2);
	cout << s1 << endl << s2;

s1的结果:abfghcde

3. 删除字符串

函数原型:string& erase (size_t pos = 0, size_t len = npos);
功能:从字符串第pos的位置删除len 的长度

    //删除字符串
    string s1 = "abcde";
	s1.erase(2, 4);

s1输出结果:ab
注意:删除字符串最多删除到字符串尾

4.提取子字符串

函数原型:string substr (size_t pos = 0, size_t len = npos) const;
功能:提取从pos开始len长度的字符串;

//提取子字符串
    string s1 = "abcde";
	string s2 = s1.substr(1, 3);

s2的结果为:bcd
注意:如果 pos 越界,会抛出异常;
如果 len 越界,会提取从 pos 到字符串结尾处的所有字符。

5.字符串查找

函数原型:size_t find (const string& str, size_t pos = 0) const; size_t find (const char* s, size_t pos = 0) const;
功能:从pos的位置查找str字符串,返回第一次出现str字符串的下标。

//字符串查找
    string s1 = "abcde";
	string s2 = "bcd";
	cout << s1.find(s2, 0);

输出结果:1
如果没有查找到,则返回一无穷大值:4294967295

二、常用算法

STL里面有很多包装好的函数可以直接实现,这样我们就省去了很多的时间,一般包含在< algorithm >这个头文件里面。

1.排序函数

sort (first, last):头文件:< algorithm >
对容器或普通数组中 [first, last) 范围内的元素进行排序,默认进行升序排。
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
可以看到,还有第三个参数,这个参数可以自定义排序顺序。具体操作:
代码如下(示例):

#include <iostream>
#include <algorithm>
using namespace std;
int cmp(int a, int b) {
	return a < b;
}
int main() {
	int a[9] = { 10,1,6,5,7,8,3,4,9 };
	sort(a, a + 9);
	for (int i = 0; i < 9; i++) cout<<a[i]<<" ";
	return 0;
}

输出:1 3 4 5 6 7 8 9 10
当然,sort()还可以对结构体数组进行排序,这个如何实现呢?举个栗子哈:

#include <iostream>
#include <algorithm>
using namespace std;
typedef struct {
	int x;
	int y;
}Stu;
int cmp(Stu a , Stu b) {
	return a.x < b.x;
}
Stu stu[10];
int main() {
	for (int i = 0; i < 10; i++)
		cin >> stu[i].x >> stu[i].y;
	sort(stu, stu + 10, cmp);
	for (int i = 0; i < 10; i++)
	    cout << stu[i].x << " " << stu[i].y;
	return 0;
}

这样就可以根据x的值对结构体进行排序啦。

2.二分查找

函数原型:

//查找 [first, last) 区域内是否包含 val
bool binary_search (ForwardIterator first, ForwardIterator last,
                      const T& val);
//根据 comp 指定的规则,查找 [first, last) 区域内是否包含 val
bool binary_search (ForwardIterator first, ForwardIterator last,
                      const T& val, Compare comp);

功能:从第first个元素到第last元素二分查找,返回值是bool类型的值,如果有返回true,如果没有返回false。
注意:第一个参数和第二个参数可以是迭代器,也可以是数组的地址;
我们可以 自己写一个二分:
递归写法:

int my_search(int a[], int left, int right, int x) {
	if (left == right) {
		if (a[left] == x)
			return left + 1;
		else
			return -1;
	}
	int mid = (right + left) / 2;
	if (a[mid] >= x) my_search(a, left, mid, x);
	else            my_search(a, mid + 1, right, x);
}

非递归写法:

int my_search(int a[], int left, int right, int x) {
	int mid;
	while (left != right) {
		mid = (right + left) / 2;
		if (a[mid] >= x)
			right = mid;
		else
			left = mid + 1;
	}
	return left;
}

3.前缀和与差分

前缀和:即在原数组数组的基础上创建一个新的数组,在求一维数组某一段的和或者二维数组某一片的和的时候操作更加简便。
一维数组前缀和
原数组:a[N];下标从1开始;
前缀和数组构建:s[N];
当i=1时;s[1] = a[1];
当i>=1时;s[i] = s[i-1] = a[i];
二维前缀和数组
原数组:a[N][N];
前缀和数组构建:s[N];
i=1,j=1: s[1][1] = a[1][1];
i>=1,j>=1: s[i][j] = a[i][j] + s[i][j-1] + s[i-1][j] - s[i-1][j-1];
大家是不是在想这个有啥用呢?
来做一做这道题就会深有体会了: 202104-2领域均值
差分数组
差分数组:即该数组的元素为原数组该位置的值与原数组前一个元素的值的差。
原数组:a[N];
差分数组:
i=1: c[1]=a[1];
i>=1: c[i]=a[i]-a[i-1];
差分数组有什么用呢?
当我们想对原数组大量的进行的某一个范围中的所有元素进行增加一个值,或者减少一个值这种操作的时候,我们只需要对差分数组进行一次操作就可以实现,而不用对原数组进行遍历操作实现。
例如:原数组a[5]={0,1,2,3,4}我们想对第1个到第3个位置的元素加上5;
我们先后构建差分数组c[N];然后直接令
c[1]+=5;c[3+1]-=5
;再利用差分数组用前缀和的方式求出原数组,就可以实现了,大家动脑一想这不是麻烦了吗?当然不是,当我们需要进行大量的这种操作的时候,这样就大大地减少了时间复杂度。
大家可以试试这道题:铺地毯
如果之前没有看过前缀和与差分大家可以看看这个文章:前缀和与差分

4.dp动态规划

动态规划是什么呢?
动态规划,即在求解问题结果的时候,上一步求解的答案作为下一步求解答案的参考,这就是动态规划的思想,也就是把整个问题能分成更小的子问题,再去求解的过程,有分治和贪心的思想,但是又不完全一样。
动态规划最重要的是什么呢?
当然是动态转移方程,如果推出了动态转移方程,那么题目就解出来一半了。
来几个栗子大家可以练习一下:
线性动态规划:合唱队形
背包动态规划:采药 榨取kkksc03
-------------------- 采药题解
--------------------榨取kkksc03题解(推销一下自己哈哈)
还有区间动态规划,树形动态规划等等,当然以上都是比较基础简单的动态规划,奉上题单动态规划基础题单
更难的状态压缩动态规划,倍增优化动态规划 ,数据结构优化动态规划,单调队列优化动态规划, 斜率优化动态规划,决策单调性优化动态规划,数位统计类动态规划, 轮廓线动态规划等等;
在此奉上一个题单动态规划提升题单

5.搜索

有待更新【狗头保命】

三.STL常用类

1.pair

类模板:template<class T1,class T2> struct pair
T1:第一个参数的类型,T2:第二个参数的类型
两个值可以分别用pair的两个公有函数first和second访问。

#include <iostream>
using namespace std;
pair<int, int>stu[10];
int main() {
	for (int i = 0; i < 10; i++)
		cin >> stu[i].first >> stu[i].second;
	for (int i = 0; i < 10; i++)
		cout << stu[i].first << stu[i].second;
	return 0;
}

2.容器deque

deque:双向队列,我们可以调用它的成员函数,对它的队列头和队列尾进行插入和删除操作,还可以调用其他成员函数清空队列:erase(),求队列的长度:size()等等;

//deque常用成员函数
#include <iostream>
#include <deque>
using namespace std;
deque<int>deq;
int main(){
	//在队列尾添加数据
	deq.push_back(1);
	deq.push_back(2);
	deq.push_back(3);
	//在队列头添加数据
	deq.push_front(0);
	//在队列尾删除数据
	deq.pop_back();
	//在队列头删除数据
	deq.pop_front();
	for (auto i = deq.begin(); i != deq.end(); i++)
		cout << *i << " ";
	return 0;
}

输出:1 2
注意:deque是随机迭代器,他可以随机访问单个元素。

2.容器适配器stack

这个大家应该都不陌生吧,没错,就是数据结构中的栈,STL把栈封装成了一个容器。

成员函数功能
empty()当 stack 栈中没有元素时,该成员函数返回 true;反之,返回 false。
size()返回 stack 栈中存储元素的个数。
top()返回一个栈顶元素的引用,类型为 T&。如果栈为空,程序会报错。
push(const T& val)先复制 val,再将 val 副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。
push(T&& obj)以移动元素的方式将其压入栈顶。这是通过调用底层容器的有右值引用参数的 push_back() 函数完成的。
pop()弹出栈顶元素。
swap(stack & other_stack)将两个 stack 适配器中的元素进行互换,需要注意的是,进行互换的 2 个 stack 适配器中存储的元素类型以及底层采用的基础容器类型,都必须相同。
#include <iostream>
#include <stack>
using namespace std;
stack<int>S1;
stack<int>S2;

int main() {
	S1.push(1);
	S1.push(2);
	S1.push(3);
	
	S2.push(4);
	S2.push(3);
	S2.push(2);
	S2.push(1);

	swap(S1, S2);
	cout << "栈S1的长度为:" << endl;
	cout << S1.size() << endl;
	cout << "栈S2的长度为:" << endl;
	cout << S2.size() << endl;
	
	cout << "栈S1中的元素依次出栈为:" << endl;
	while (!S1.empty()) {
		cout << S1.top();
		S1.pop();
	}
	cout << endl;
	cout << "栈S2中的元素依次出栈为:" << endl;
	while (!S2.empty()) {
		cout << S2.top();
		S2.pop();
	}
	return 0;
}

输出:

栈S1的长度为:
4
栈S2的长度为:
3
栈S1中的元素依次出栈为:
1234
栈S2中的元素依次出栈为:
321

注意:stack不支持迭代器

3.容器适配器queue

queue:队列,后进先出的一种数据结构,也被STL封装成了容器适配器。

成员函数功能
empty()如果 queue 中没有元素的话,返回 true。
size()返回 queue 中元素的个数。
front()返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
back()返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
push(const T& obj)在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
emplace()在 queue 的尾部直接添加一个元素。
push(T&& obj)以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
pop()删除 queue 中的第一个元素。
swap(queue &other_queue)将两个 queue 容器适配器中的元素进行互换,需要注意的是,进行互换的 2 个 queue 容器适配器中存储的元素类型以及底层采用的基础容器类型,都必须相同。

这个呢和stack是一样的,代码大家可以自己下去联系一下啦;
注意:queue也不支持迭代器;

4.关联式容器map

map是双向迭代器,我们常用map来实现哈希表,这是map的定义:

template < class Key,                                     // 指定键(key)的类型
           class T,                                       // 指定值(value)的类型
           class Compare = less<Key>,                     // 指定排序规则
           class Alloc = allocator<pair<const Key,T> >    // 指定分配器对象的类型
           > class map;

我们一般会用到前面两个参数, Key 和 T 分别表示键和值,大家可以尝试一下下面这道题:可能对map有更深刻的理解:- P1102 A-B 数对

#include <iostream>
#include <map>
#include <string>
#include<algorithm>
using namespace std;
/*map第三个参数,是对map中元素的键进行排序:
* greater<key>:降序;less<key>:升序
* key为键的类型
*/
map<int, string, greater<int>>mymap{ {20,"STL教程"},{10,"C语言教程"} };
int main() {
	cout << "根据键直接查找键的值:" << endl;
	cout << mymap[10] << endl;
	cout << mymap[20] << endl;
	cout << "输出键值对:" << endl;
	for (auto i = mymap.begin(); i != mymap.end(); i++)
		cout << i->first <<" "<< i->second << endl;;
	return 0;
}

注意:使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。
unordered_map容器: 顾名思义,即无须关联式容器,和map的区别就是,他不会根据键自动进行排序,也被称为哈希容器。
大家如果有兴趣可以去看下:unordered_map详解

总结

当然,这么多东西,只看不练是不行的,一定要将这些东西用到题中,用到代码中,最后奉上一个提单:能力全面提升提单

后面还有待更新。。。。。。

  • 5
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小光学嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值