STL总结

一、vector

介绍:

vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。

在局部区域不可以长数组,但是可以开vector。

使用的时候需要加上头文件

#include<vector>


初始化:

没有指定大小的初始化:

	vector<int> a; //相当于开了一个名称为a的,存放整型的数组
	vector<double> b; //相当于开了一个名称为b的,存放浮点数的数组。
	vector<pair<int,int>> c; //存放pair的数组,常用与存点。
	vector<pair<pair<int,int>,int>> d;
	//常用于需要输出步数的搜索,前面存放点,后面存放当前步数。
	vector<pair<pair<int,int>,pair<int,int>>> e;
	//常用于需要输出路径的搜索,两个pair分别存放当前路径点和路径中的上一个点

指定大小的初始化:

	int n;
	vector<int> a(n); //开一个初始化长度为n的整型数组
	vector<int> b(n, 1); //开一个初始化长度为n,且每个元素都为1的整形数组
	vector<int> c{1, 2, 3}; //开一个初始化中有三个元素1,2,3的整形数组
	vector<int> d(a); //开一个和a一模一样的数组

固定长度的二维数组创建:

	int n, m;
	vector<vector<int>> a(n, vector<int> (m, 1));
	//这句话的意思相当于是说,开了一个有n行,m列,且初始时每个元素都是1的二维数组
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> a[i][j];
		}
	}

不固定长度的二维数组创建(每一行的元素个数可以不同):

	int n; //表示有n行
	vector<vector<int>> b;
	for (int i = 0; i < n; i++) {
		vector<int> temp; 
		int m; cin >> m; //临时开一个数组,这个数组有m个元素
		for (int j = 0; j < m; j++) {
			int x; cin >> x;
			temp.push_back(x); //在临时数组中挨个插入元素
		}
		b.push_back(temp); //将这个临时数组插入到二维数组中
	}


函数调用

a.pushback(x);时间复杂度\(O(1)\):

在数组最后的位置插入元素x。

	vector<int> a;
	int x; a.push_back(x); //在数组的最后插入指定元素x;
	vector<pair<int, int>> b;
	int n, m; b.push_back({n, m});
	a.pop_back();

a.size();时间复杂度\(O(1)\):

返回数组中含有的实际数据个数。

	vector<int> a{1, 2, 3, 4, 5};
	cout << a.size();
    //输出结果为 5

a.pop_back();时间复杂度\(O(1)\):

删除数组中的最后一个元素。

	vector<int> a{1, 2, 3};
	for (int i = 0; i < a.size(); i++) cout << a[i] << " ";
	//输出结果为 1 2 3
	a.pop_back();
	for (int i = 0; i < a.size(); i++) cout << a[i] << " ";
	//输出结果为 1 2

a.empty();时间复杂度\(O(1)\):

查看数组中是否含有元素。如果是空数组,则返回真。如果有数据,则返回假。

	vector<int> a;
	if (a.empty()) cout << "空" << " ";
	else cout << "非空" << " ";
	a.push_back(1);
	if (a.empty()) cout << "空" << " ";
	else cout << "非空" << " ";
	//输出结果为 空 非空

a.front(); a.back();时间复杂度\(O(1)\):

返回数组中第一个和最后一个数据。

	vector<int> a{10, 20, 30, 40, 50};
	cout << a.front() << " " << a.back() << endl;
	//输出结果为 10 50

a.begin(); a.end();时间复杂度\(O(1)\):

返回数组中第一个数据地址。返回数组中最后一个数据的后一个位置的地址。

	vector<int> a{10, 20, 30, 40, 50};
	vector<int>::iterator it1 = a.begin();
	vector<int>::iterator it2 = a.end();
	cout << *it1 << " " << *(it2 - 1) << endl;
	//输出结果为 10 50

另,在vector中用sort的方法:

	vector<int> a{5, 2, 7, 1, 8};
	sort(a.begin(), a.end());
	for (int i = 0; i < a.size(); i++) cout << a[i] << " ";

	//对比常规数组。
	int b[5] = {5, 2, 7, 1, 8};
	sort(b, b + 5);
	for (int i = 0; i < 5; i++) cout << b[i] << " ";

	//输出结果都为 1 2 5 7 8

a.insert(it, x);时间复杂度\(O(n)\):

在迭代器it后面插入一个元素x。

	vector<int> a{10, 20, 30, 40, 50};
	a.insert(a.begin() + 1, 666);
	for (int i = 0; i < a.size(); i++) cout << a[i] << " ";
	//输出结果为 10 666 20 30 40 50

a.erase(start, end);时间复杂度\(O(n)\):

start, end为迭代器。删除数组中从start到end中的元素。包括start,不包括end。

	vector<int> a{10, 20, 30, 40, 50};
	a.erase(a.begin() + 1, a.begin() + 3);
	for (int i = 0; i < a.size(); i++) cout << a[i] << " ";
	//输出结果为 10 40 50



二、queue:

介绍:

queue是队列,先进入队列中的数据先出来,就像排队一样,先去排队的人最先排完队。

常用与单调队列或宽度优先搜索等算法。

使用的时候需要加上头文件:

#include<queue>


初始化:

	queue<int> q1;
	queue<pair<int, int>> q2;


函数调用:

q.push(x); q.pop(); q.front(); q.back(); q.size(); q.empty() 六个函数时间复杂度均为\(O(1)\)。

	queue<int> q;
	int x; q.push(x); //在队尾插入元素x
	q.pop(); //删除队首的元素
	q.push(10); q.push(20); q.push(30);
	cout << q.front() << " " << q.back() << endl;
	//返回队首元素      和    返回队尾元素
	//输出结果为 10 30
	cout << q.size() << endl;
	//返回队列中的元素个数
	//输出结果为 3

	if (q.empty()) cout << "空" << endl;
	else cout << "非空" << endl;
	//如果是空队列,则返回值为真。
	//如果队列中含有元素,则返回值为假。
	//输出结果为 非空


例题:

①:acwing 829. 模拟队列

原题指路:829. 模拟队列 - AcWing题库

代码一:(模拟版)

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int q[N];
int hh;
int tt = - 1;

void solve() {
    int m; cin >> m;
    while(m--) {
        string op;
        cin >> op;

        if (op == "push") {
            int x; cin >> x;
            q[++tt] = x;
        }
        else if (op == "pop") {
            hh++;
        }
        else if(op == "empty") {
            if (hh > tt) cout << "YES" << endl;
            else cout << "NO" << endl;
        }
        else {
            cout << q[hh] << endl;
        }
    }
}

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

代码二:(队列版)

#include<bits/stdc++.h>
using namespace std;

void solve() {
    queue<int> q;
    int m; cin >> m;
    while(m--) {
        string op; cin >> op;

        if (op == "push") {
            int x; cin >> x;
            q.push(x);
        }
        else if (op == "pop") {
            q.pop();
        }
        else if (op == "empty") {
            if (q.empty()) cout << "YES" << endl;
            else cout << "NO" << endl;
        }
        else {
            cout << q.front() << endl;
        }
    }
}

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

②:Codeforces Round 295 (Div. 2) B

原题指路:Problem - 520B - Codeforces

代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;

const int N = 2e4 + 10;

int n, m;
bool vis[N];
queue<PII> q;

void init() {
    memset(vis, false, sizeof(vis));
}

void bfs() {
    q.push({n, 0});
    vis[n] = true;

    while(q.size()) {
        int a = q.front().first * 2;
        int b = q.front().first - 1;
        int step = q.front().second;
        q.pop();

        if (a == m || b == m) {
            cout << step + 1 << endl;
            return;
        }

        if (!vis[a] && a >= 1 && a <= N) {
            vis[a] = true;
            q.push({a, step + 1});
        }
        if (!vis[b] && b >= 1 && b <= N) {
            vis[b] = true;
            q.push({b, step + 1});
        }

    }

}

void solve() {
    cin >> n >> m;

    init();

    if (n == m) {
        cout << 0 << endl;
        return;
    }

    if (n > m) {
        cout << n - m << endl;
        return;
    }

    bfs();
}

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



三、deque

介绍:

双向队列。上一个的队列,只能够删除队首,在队尾插入。而双向队列,能够支持队首和队尾删除和插入的操作。

常用于滑动窗口等问题。

使用的时候需要加上头文件:

#include<deque>


初始化:

	deque<int> q;


函数调用:

q.push_back(); q.push_front(); q.size(); q.front(); q.back(); q.pop_back(); q.pop_front(); q.empty(); 的时间复杂度均为\(O(1)\)。

	deque<int> q;
	q.push_back(50); //在队列尾巴插入元素 50
	q.push_back(666); //在队列尾巴插入元素 666
	q.push_front(10); //在队列头部插入元素 10
	q.push_back(5201314); //在队列尾巴插入元素 5201314
	q.push_back(2333); //在队列尾巴插入元素 2333

	cout << q.size() << endl; //返回双向队列中的元素个数。
	//输出结果为 5
	for (int i = 0; i < q.size(); i++) cout << q[i] << " \n"[i == q.size() - 1];
	//输出结果为 10 50 666 5201314 2333

	cout << q.front() << " " << q.back() << endl;
	//返回队列头部元素   和   返回队列尾巴元素
	//输出结果为 10 2333

	q.pop_front(); q.pop_back();
	//删除队首元素 和 删除队尾元素
	for (int i = 0; i < q.size(); i++) cout << q[i] << " \n"[i == q.size() - 1];
	//输出结果为 50 666 5201314

	if (q.empty()) cout << "空" << " ";
	else cout << "非空" << " ";
	q.clear(); //清空双向队列
	if (q.empty()) cout << "空" << endl;
	else cout << "非空" << endl;
	//如果是空双向队列,则返回值为真。
	//如果双向队列中含有元素,则返回值为假。
	//输出结果为 非空 空

	q.push_back(10); q.push_back(20); q.push_back(30); q.push_back(40);
	q.push_back(50); q.push_back(60); q.push_back(70); q.push_back(80);
	for (int i = 0; i < q.size(); i++) cout << q[i] << " \n"[i == q.size() - 1];
	//输出结果为 10 20 30 40 50 60 70 80
	q.erase(q.begin() + 2);
	//删除迭代器所在的元素
	for (int i = 0; i < q.size(); i++) cout << q[i] << " \n"[i == q.size() - 1];
	//输出结果为 10 20 40 50 60 70 80
	q.erase(q.begin() + 3, q.begin() + 6);
	//删除两个迭代器之间的元素,包括前面的那个元素,不包括后面的那个元素。
	for (int i = 0; i < q.size(); i++) cout << q[i] << " \n"[i == q.size() - 1];
	//输出结果为 10 20 40 80


例题:滑动窗口

acwing 154. 滑动窗口

原题指路:154. 滑动窗口 - AcWing题库

洛谷 P1886 滑动窗口 /【模板】单调队列

原题指路:P1886 滑动窗口 /【模板】单调队列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

代码一:(模拟版)

#include<bits/stdc++.h>
using namespace std;

//求滑动窗口中的最小值的时候,只要把所有逆序的数字删掉
//那么队列中留下的就一定是一个单调上升的队列。
//原因:左边的数字先出去,左边的这个数字还比右边的某个数字大,那么这个左边的数字就没有输出的可能性

const int N = 1e6 + 10;
int n, k;
int a[N]; //输出的数字串
int q[N]; //单调队列,存的是下标

void solve() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }

    int hh = 0; int tt = -1;    //定义队头和队尾。
    for (int i = 0; i < n; i++) {
        //判断队头是否已经滑出窗口。
        //队列里存的其实是下标。
        if (hh <= tt && i - k + 1 > q[hh]) hh++; //队尾往前走一步,如果能走的话
        while (hh <= tt && a[q[tt]] >= a[i]) tt--; //如果队列中的数比新的数大的话就可以走了。
        q[++tt] = i; //新加入来的一个数。
        if (i >= k - 1) cout << a[q[hh]] << " ";
    }

    cout << endl;

    hh = 0, tt = -1;
    for (int i = 0; i < n; i++) {
        if (hh <= tt && i - k + 1 > q[hh]) hh++;
        while (hh <= tt && a[q[tt]] <= a[i]) tt--;
        q[++tt] = i;
        if (i >= k - 1) cout << a[q[hh]] << " ";
    }
}

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

代码二:(双向队列版)

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;

int a[N];
int n, k;

void solve() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }

    deque<int> q;
    for (int i = 0; i < n; i++) {
        if (!q.empty() && i - k + 1 > q.front()) q.pop_front();
        while(q.size() && a[q.back()] >= a[i]) q.pop_back();
        q.push_back(i);
        if (i >= k - 1) cout << a[q.front()] << " ";
    }

    cout << endl;

    q.clear();
    for (int i = 0; i < n; i++) {
        if (!q.empty() && i - k + 1 > q.front()) q.pop_front();
        while(q.size() && a[q.back()] <= a[i]) q.pop_back();
        q.push_back(i);
        if (i >= k - 1) cout << a[q.front()] << " ";
    }

}

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



四、priority_queue

介绍:

优先队列除了和常规队列一样数据先进先出以外,优先队列中的元素还被赋予了优先级,并且保证了队首的元素是优先级最高的。它是通过堆来实现优先级这一功能的。

常用于堆优化版dijkstra算法,动态中位数,一些模拟题当中。

使用的时候需要加上头文件:

#include<queue>


初始化:

普通优先队列初始化:

	priority_queue<int> q1; //默认是大根堆,也就是说队首的元素是最大值
	priority_queue<int, vector<int>, less<int>> q2; //大根堆,和上面那个一样
	priority_queue<int, vector<int>, greater<int>> q3; //小根堆,队首元素是最小值。
	//构造的时候,第一个元素表示数据类型,第二个元素是容器,第三个元素是优先级。

结构体的优先队列初始化:

其中要注意的是,优先队列的“排序”方式和sort不太一样,按照下方代码中的cmp函数,如果是sort的话,那么就是从小到大排序,但是在优先队列中的话就是由大到小排序。也就是说,下面的排序方式是,先看node中的x,再看y。x大的排在前面,x相同的话那么y大的排在前面。

struct node{
	int x, y;
};

struct cmp {
	bool operator() (const node &a, const node &b) {
		if (a.x != b.x) return a.x < b.x;
		return a.y < b.y;
	}
};

void solve() {
	priority_queue<node, vector<node>, cmp> q;
}

pair类型的优先队列初始化:

默认构造是按照先比较pair中的first元素,将first元素大的放前面,然后再比较second元素,将second元素大的放前面。

	priority_queue<pair<int, int>> q;
	q.push({1, 4});
	q.push({5, 2});
	q.push({1, 2});
	q.push({1, 3});
	q.push({3, 3});
	while(q.size()) {
		cout << q.top().first << " " << q.top().second << endl;
		q.pop();
	}
	/*
	输出结果为:
	5 2
	3 3
	1 4
	1 3
	1 2
	*/


函数调用:

	priority_queue<int> q;
	q.push(5); //在双向队列中插入元素 5
	q.push(1); q.push(7); q.push(8); q.push(666);
	cout << q.size() << endl; //返回优先队列中元素个数
	if (!q.empty()) cout << "非空" << endl; //查询优先队列是否为空。
	while(q.size()) {
		cout << q.top() << " "; //返回队首元素。
		q.pop(); //删除队首元素。
	}
	/*
	输出结果为:
	5
	非空
	666 8 7 5 1
	*/


例题:

①:acwing 850. Dijkstra求最短路 II

原题指路:850. Dijkstra求最短路 II - AcWing题库

代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;

//堆优化版本的dijkstra算法
//也就是在朴素的版本上进行了修改
//可以选择用手写堆来优化,能保证堆里面的元素个数是正好n个,但是就是会麻烦一点。时间复杂度mlogn
//也可以用C++里面的优先队列,但是由于不支持修改任意一个操作,所以每次修改都只能往里面插入新的数
//所以优先队列里面的元素就可能会有m个,但是就是会方便很多
//举个例子就是两个点的距离可能会存在两个数。时间复杂度mlogm ≈ mlogn (∵m<n^2,logm^2=2logm)
//堆优化版本的dijkstra适用于稀疏图

//用邻接表的话,就不需要对重边进行处理了,因为算法他本身就会把这个事情处理掉了。

//用堆来维护所有点的这个距离,维护距离的时候还需要知道结点的编号是多少,所以堆里面存的其实是一个pair

const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10;

int n, m;
int h[N], w[N], e[N], ne[N], idx; //改成邻接矩阵的形式
int dist[N]; //表示从i号点走到起点的当前的最短路的距离是多少
bool st[N]; //表示每个点的最短路的距离是不是已经确定了

void add(int a, int b, int c) {
    e[idx] = b; w[idx] = c, ne[idx] = h[a]; h[a] = idx++;
}

int dijkstra() {
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;

    priority_queue<PII, vector<PII>, greater<PII>> heap; //一维数组用vector的话就比较方便,greater是参数,这么写的话就会变成小根堆。
    heap.push({0, 1}); //上来先把一号点放进去,因为一号点是已经知道最短路的距离的了

    while (heap.size()) { //保证队列里面不空的
        auto t = heap.top(); //每次找到距离最小的点就是堆的起点
        heap.pop();

        int ver = t.second; //表示这个点的点编号
        int distance = t.first; //表示这个点的距离
        if (st[ver]) continue; //如果这个点已经处理过了,说明他是冗余备份,就不用再处理它了

        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i]) { //用当前这个点的距离来更新其他点的这个最短距离。
            int j = e[i];
            if (dist[j] > distance + w[i]) {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }

    }

    if (dist[n] == INF) return -1; //假设不连通的话,就返回-1
    return dist[n]; //不然的话,就返回最短路的距离

}

void solve() {
    cin >> n >> m;
    memset(h, -1, sizeof(h)); //初始化邻接表

    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }

    int t = dijkstra();
    cout << t << endl;

}

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

②:洛谷 P1168 中位数

原题指路:P1168 中位数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

代码:

#include<bits/stdc++.h>
using namespace std;

//用一个漏斗堆来维护
//上面是一个(倒着的)小根堆,存的是大的那一半元素
//下面是一个(正着的)大根堆,存的是小的那一半元素
//保证插入偶数个数字之后,两个堆内的元素个数数量相同
//插入奇数个数字的时候,下面的堆比上面的那个堆多一个元素
//那么中间的那个点,就是中位数,他小于上面的那一半元素,大于下面的那一半元素

void solve() {
    int n; cin >> n;

    priority_queue<int> down;
    priority_queue<int, vector<int>, greater<int>> up;

    for (int i = 1; i <= n; i++) {
        int x; cin >> x;

        if (down.empty() || x <= down.top()) down.push(x);
        else up.push(x);

        if (down.size() > up.size() + 1) up.push(down.top()), down.pop();
        if (up.size() > down.size()) down.push(up.top()), up.pop();

        if (i % 2) {
            cout << down.top() << endl;
        }
    }
}

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

③:Codeforces Round 840 (Div. 2) and Enigma 2022 - Cybros LNMIIT B

原题指路:Problem - B - Codeforces

代码:

#include<bits/stdc++.h>
using namespace std;
#define CaseT int CaseT; cin >> CaseT; while(CaseT--)
typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
int n, k;
bool st[N];

//用小根堆来维护
//那么一开始的时候,第一回合是打k点伤害,打到不能打死怪物为止
//打完后把死掉的怪物删掉
//打完之后,攻击力减少小根堆的堆顶
//那么第二轮再打的时候,攻击力就是k + (k - top)
//以此类推,那么就不用像一开始那样,通过模拟一遍遍的扫,然后一次次的降低每个怪物的血量
//而是变成提升每轮的攻击力,来判断能不能往后继续进攻。
//所以k就有了两个作用,一个是用来每轮增加变成攻击力,一个是每轮减少,计算一共减少的攻击力,充当生命值的作用。

void solve() {
    memset(st, false, sizeof(st));

    cin >> n >> k;
    PII mon[n];

    for (int i = 0; i < n; i++) cin >> mon[i].first;
    for (int i = 0; i < n; i++) cin >> mon[i].second;

    sort(mon, mon + n);
    priority_queue<PII, vector<PII>, greater<PII>> q; //小根堆维护怪物的攻击力

    for (int i = 0; i < n; i++) q.push({mon[i].second, i});

    int temp = k; //temp表示当前的攻击力
    bool fight = true; //能不能打赢
    int j = 0;
    while(1) {
        while(j < n && temp >= mon[j].first) st[j++] = true; //如果当前攻击力大于怪物血量,那么就说明能打掉这个怪物
        if (j >= n) break; //怪物全部打掉之后就退出循环

        while(st[q.top().second]) q.pop(); //如果这个怪物被打掉了,那么就删掉他

        if (k - q.top().first <= 0) { //到怪物的回合了,炮台生命值减去小根堆的堆顶
            fight = false; //炮台被干掉了,战斗失败
            break;
        }

        temp += k - q.top().first; //炮台的攻击力增加一轮的输出并减去减少的伤害值
        k -= q.top().first; //炮台的生命值减去怪物伤害的最小值

    }

    if (fight) cout << "YES" << endl;
    else cout << "NO" << endl;
}

int main() {
    CaseT
    solve();
    return 0;
}



五、stack

介绍:

栈就像是一个瓶子一样的容器,最先进去的数据被压在瓶子底部最后出来,最后进去的数据放在瓶子的表面最先出来。因此栈是先进后出,后进先出。

常用于单调栈等数据结构。

使用的时候需要加上头文件:

#include<stack>


函数调用:

	stack<int> s;
	s.push(10); //插入元素 10;
	s.push(20); s.push(30); s.push(40); s.push(50);
	cout << s.top() << endl;
	//返回栈顶元素,输出结果为 50
	s.pop(); //删除栈顶元素。
	cout << s.top() << endl;
	//输出结果为 40
	cout << s.size() << endl;
	//返回栈中的元素个数,输出结果为 4
	if (s.empty()) cout << "空" << endl;
	else cout << "非空" << endl;
	//查询栈是否为空。输出结果为 非空


例题:acwing 830. 单调栈

原题指路:830. 单调栈 - AcWing题库

代码一:(模拟版)

#include<bits/stdc++.h>
using namespace std;

//题目:给出一串数字,然后输出每个数字的左边第一个小于它的数字大小(思路:用单调栈滑动窗口)

const int N = 1e5 + 10;
int stk[N];
int tt; //栈中的元素个数。

void solve() { //先进的后出,后进的先出来。
    int n; cin >> n;
    for (int i = 0; i < n; i++) {
        int x; cin >> x;
        while(tt && stk[tt] >= x) tt--; //如果说这个栈不为空,而且栈顶的数大于x,那么这个数就再也不会用到了。
        if (tt) cout << stk[tt] << " ";
        else cout << -1 << " ";
        stk[++tt] = x;
    }
}

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

代码二:(栈版)

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

void solve() {
    int n; cin >> n;
    stack<int> s;
    for (int i = 0; i < n; i++) {
        int x; cin >> x;
        while(!s.empty() && s.top() >= x) s.pop();
        if (s.size()) cout << s.top() << " ";
        else cout << -1 << " ";
        s.push(x);
    }
}

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



六、string

介绍:

可以像int, double类型一样,理解为字符串型,和char的字符串差不多。

使用的时候需要加上头文件:

#include<string>


初始化:

	string s1; //初始化空字符串
	string s2 = "Autumn_goose"; //初始化这一字符串
	string s3("233333"); //和上面那个一样
	string s4(5, '6'); //相当于初始化一个 含有5个'6'这一字符 的字符串
	string s5(s2, 5); //复制s2字符串从第5个(因为下标从0开始所以实际上是第6个)开始到最后一个字符
	cout << s4 << "   " << s5 << endl;
	//输出结果为: 66666   n_goose


语法使用:

一个字符串中单个字符的访问:

	string s = "abcdef";
	for (int i = 0; i < 6; i++) cout << s[i] << " ";
	//输出结果为 a b c d e f

字符串数组的创建以及单个字符的访问:

	int n; string s[n];
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < s[i].length(); j++) {
			cout << s[i][j];
		}
	}

字符串的相加:

相当于直接在一个字符串后面再连接一个字符串。

	string s1 = "qiuyan";
	string s2 = "shishuaige";
	string s3 = s1 + s2;
	cout << s3 << endl;
	//输出结果为 qiuyanshishuaige

字符串的比较:

	string s1 = "aa";
	string s2 = "aaa";
	string s3 = "abbbb";
	string s4 = "bbb";
	string s5 = "123";
	string s6 = "111";
	if (s1 < s2) cout << "s1 < s2" << endl;
	if (s1 < s3) cout << "s1 > s3" << endl;
	if (s2 < s4) cout << "s2 < s4" << endl;
	//由前三个比较可知,字符串比较是从前到后挨个字符比较,直到两个字符不相同时(或其中一个已经没了),字典序大的(或者字符串长度长的)字符串更大。
	if (s5 > s6) cout << "s5 > s6" << endl;
	if (s4 > s5) cout << "s4 > s5" << endl;
	//不只是字母可以比较,数字和数字之间,数字和字母之间等等都可以比较。按照字典序进行排序。


函数调用:

	string s = "qiuyan666";
	cout << s.length() << " " << s.size() << endl;
	//返回该字符串的长度 9 ,两个函数是差不多一样的。

	s.push_back('A'); //在字符串的最后面加上一个字符'A'。
	cout << s << endl; //qiuyan666A

	s.insert(s.begin() + 5, 'n'); //在字符串的某个位置加上字符
	cout << s << endl; //qiuyann666A

	s.append("handsome"); //在字符串的后面再加上一个字符串
	// 相当于 s += "handsome"; 的操作。
	cout << s << endl; //qiuyann666Ahandsome

	s.erase(s.begin() + 10); //删除字符串中某个位置的字符。
	cout << s << endl; //qiuyann666handsome

	s.erase(6, 4); //删除这个字符串从第6位开始往后长度为4的字符。
	cout << s << endl; //qiuyanhandsome

	s.erase(s.begin() + 3, s.begin() + 6);
	//删除字符串某个区间上面的字符,包括前面的,不包括后面的。
	cout << s << endl; //qiuhandsome

	cout << s.substr(3, 6) << endl; //handso
	//截取字符串中从第3未开始长度为6的子串

	cout << s.find("hand", 1) << endl; //从第1个字符开始向后查找"hand"这个字符串
	//如果找到了,返回第一个找到的该字符串的首个字符的位置 3
	cout << s.find("fsffajog", 1) << endl;
	//如果没找到会输出18446744073709551615,也就是 2 ^ 64 - 1
	string a = "hhahhahhahhhahhahha";
	cout << a.find("hha") << endl; //只返回找到的第一个字符串的首字母下标 0

	cout << s.find('n', 0) << endl; //从下标为0的字符开始往后寻找字符串中是否存在'n'这个字符。
	//如果找到了就输出第一个找到的该字符的下标。

	cout << s.rfind("hand", 9) << endl; //从后往前遍历查找该字符串第一次出现的首字符的下标 3
	cout << a.rfind("hha", 18) << endl; //输出结果为16

	cout << s.rfind('n', 9) << endl; //从后往前遍历找该字符


排序:

	string s = "sgakfhashh";
	sort(s.begin(), s.end());
	cout << s << endl;
	//输出结果为 aafghhhkss



七、map

介绍:

map就像是函数中的映射关系一样,每个键都会对应一个值。或者也可以理解为名字和学号之间的关系,每一个名字都会对应一个学号。比如 秋雁 对应 666。 春雁 对应 233。同时,map会根据键的顺序从小到大自动排序。

使用的时候需要加上头文件:

#include<map>


初始化:

	map<int, int> m1;
	map<string, int> m2;
	struct node {};
	map<string, node> m3;


函数调用:

插入与删除:

	map<string, int> m;

	m["xiayan"] = 520;
	m["qiuyan"] = 666;
	m.insert({"chunyan", 233});
	m.insert({"dongyan", 1314});

访问方式:

	cout << m["qiuyan"] << endl; //直接访问某个键的值

	for (auto i : m) { //智能指针访问
		cout << i.first << " " << i.second << endl;
	}

	map<string, int>::iterator it; //迭代器访问
	for (it = m.begin(); it != m.end(); it++) {
		cout << it->first << " " << it->second << endl;
	}

	for (auto [x, y] : m) { //注:c++17以后版本才有这高端玩意。
		cout << x << " " << y << endl;
	}

常用函数:

m.size(); m.erase(it) 的时间复杂度为\(O(1)\)。 m.clear() 的时间复杂度为\(O(n)\)。

m.erase(key) 的时间复杂度为\(O(logN)\);

	cout << m.size() << endl;
	//返回map中有几对键值。输出结果为 4

	map<string, int>::iterator it1 = m.find("qiuyan");
	cout << it1->first << " " << it1->second << endl;
	//如果这个数据存在,则返回这个数据所在迭代器。

	cout << m.count("qiuyan") << " " << m.count("bucunzai") << endl;
	//如果存在的话就返回1,不存在的话就返回0。

	m.erase(it1); //删除这个迭代器对应的一对键和值
	m.erase("chunyan"); //删除这个键和其所对应的值。
	cout << m.size() << endl; //输出结果为2
	m.clear(); //清空map
	cout << m.size() << endl; //输出结果为0

	m["huang"] = 5; m["hao"] = 8; m["zi"] = 24;
	m["pi"] = 12; m["ka"] = 1; m["qiu"] = 10;
	for (auto i : m) { //智能指针访问
		cout << i.first << " " << i.second << endl;
	}
	/*
	输出结果为:
	hao 8
	huang 5
	ka 1
	pi 12
	qiu 10
	zi 24
	*/
	map<string, int>::iterator it2 = m.find("ka");
	map<string, int>::iterator it3 = m.find("zi");
	m.erase(it2, it3); //删除一个区间内的键值
	for (auto i : m) { //智能指针访问
		cout << i.first << " " << i.second << endl;
	}
	/*
	输出结果为:
	hao 8
	huang 5
	zi 24
	*/

	if (m.empty()) cout << "空" << endl;
	else cout << "非空" << endl;
	//查询map是否为空。输出结果为 非空


例题:

①:2022 Jiangsu Collegiate Programming Contest A

原题指路:Problem - A - Codeforces

代码:

#include<bits/stdc++.h>
using namespace std;

void solve() {
    int n; cin >> n;
    map<string, vector<string>> kill;
    for (int i = 1; i <= n; i++) {
        string a, b; cin >> a >> b;
        kill[a].push_back(b);
    }
    for (auto i : kill) {
        for (int j = 0; j + 5 <= i.second.size(); j++) {
            if (set<string>(i.second.begin() + j, i.second.begin() + j + 5).size() == 5) {
                cout << "PENTA KILL!" << endl;
                return;
            }
        }
    }
    cout << "SAD:(" << endl;
}

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

②:洛谷 P2580 于是他错误的点名开始了

原题指路:P2580 于是他错误的点名开始了 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

代码:

#include<bits/stdc++.h>
using namespace std;

map<string, int> a;

void solve() {
    int n; cin >> n;

    while(n--) {
        string s; cin >> s;
        a[s] = 1;
    }

    int m; cin >> m;
    while(m--) {
        string s; cin >> s;
        if (a[s] == 1) {
            cout << "OK" << endl;
            a[s] = 2;
        }
        else if (a[s] == 2) {
            cout << "REPEAT" << endl;
        }
        else cout << "WRONG" << endl;
    }
}

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

③:Codeforces Round 847 (Div. 3) D

原题指路:Problem - D - Codeforces

代码:

#include<bits/stdc++.h>
using namespace std;
#define CaseT int CaseT; cin >> CaseT; while(CaseT--)

void solve() {
    int n; cin >> n;
    vector<int> a(n);
    map<int, int> bin; //bin[i]的值表示i这个数字出现了多少次

    for (int i = 0; i < n; i++) {
        cin >> a[i];
        bin[a[i]]++;
    }

    sort(a.begin(), a.end());

    int ans = 0;
    for (int i = 0; i < n; i++) {
        if (bin[a[i]]) {
            ans++;
            for (int k = a[i]; bin[k] != 0; k++) {
                bin[k]--;
            }
        }
    }

    cout << ans << endl;

}

int main() {
    CaseT
    solve();
    return 0;
}



八、set

介绍:

set是一个集合,里面存放的元素都不相同。当集合里面已经有某个元素,再进行插入操作的时候,就不会再插入这个元素了。同时set里面的元素会自动从大到小进行排序。用一句话来说就是set里面的元素有序且不重复。

使用的时候需要加上头文件:

#include<set>


函数调用:

插入与删除:

s.size() 的时间复杂度为\(O(1)\)。

	set<int> s;
	s.insert(5); //往set里面插入元素 5
	s.insert(100);
	cout << s.size() << endl;
	//返回set中的元素个数 2
	s.insert(5);
	cout << s.size() << endl;
	//输出结果仍然为 2。可知插入重复元素的时候是不会继续往set里面插入的

访问方式:

	for (auto i : s) { //智能指针访问
		cout << i << " ";
	}
	cout << endl;
	//输出结果为 -1 0 1 3 5 10 100 233 555 666

	set<int>::iterator it1; //迭代器访问
	for (it1 = s.begin(); it1 != s.end(); it1++) {
		cout << *it1 << " ";
	}
	cout << endl;

常用函数:

s.clear() 的时间复杂度为\(O(n)\)。

	set<int>::iterator it2 = s.find(666);
	cout << *it2 << endl;
	//查找某一元素,如果存在的话就返回该元素的迭代器。

	cout << s.count(555) << endl;
	cout << s.count(111) << endl;
	//查找某一元素是否出现,出现的话输出1,没出现的话输出0。

	s.erase(it2); //删除这个迭代器所对应的元素
	s.erase(3); //直接删除这个元素
	for (auto i : s) { //智能指针访问
		cout << i << " ";
	}
	cout << endl;
	//输出结果为 -1 0 1 5 10 100 233 555

	set<int>::iterator it3 = s.find(5);
	set<int>::iterator it4 = s.find(233);
	s.erase(it3, it4); //删除这个区间的元素。
	for (auto i : s) {
		cout << i << " ";
	}
	cout << endl;
	//输出结果为 -1 0 1 233 555

	if (s.empty()) cout << "空" << endl;
	else cout << "非空" << endl;
	//查询集合是否为空。输出结果为 非空
	s.clear(); //清空集合
	if (s.empty()) cout << "空" << endl;
	else cout << "非空" << endl;
	//输出结果为 空

改变排序规则:

	//方法一:变成从大到小排
	set<int> s1; //默认从小到大排序
	set<int, less<int>> s2; //和上面那个是一样的
	set<int, greater<int>> s3; //从大到小排序

	//方法二:重载运算符
	struct cmp {
		bool operator() (const int &x, const int &y) const {
			return x > y;
		}
	};
	set<int, cmp> s4;

	//方法三:结构体比较
	struct node {
		int x; int y;
		bool operator < (const node &a) const {
			if (x != a.x) return x < a.x;
			return y < a.y;
		}
	};
	set<node> s5;


例题:

①:Pinely Round 1 (Div. 1 + Div. 2) B

原题指路:Problem - 1761B - Codeforces

代码:

#include<bits/stdc++.h>
using namespace std;
#define CaseT int CaseT; cin >> CaseT; while(CaseT--)

const int N = 110;
int n;
int a[N];

void solve() {
    int n; cin >> n;
    set<int> s;
    for (int i = 0; i < n; i++) {
        int a; cin >> a;
        s.insert(a);
    }

    cout << (s.size() > 2 ? n : n / 2 + 1) << endl;
}

int main() {
    CaseT
    solve();
    return 0;
}

②:Codeforces Round 620 (Div. 2) B

原题指路:Problem - 1304B - Codeforces

代码:

#include<bits/stdc++.h>
using namespace std;
#define CaseT int CaseT; cin >> CaseT; while(CaseT--)
#define all(x) (x).begin(), (x).end()

//组成一个新的长的回文串只能有两种情况
//一种是本身构成回文串,然后放在新得到串的中间
//另一种就是和另一个串正好是反过来的,一左一右放两边

set<string> st;

void solve() {
    int n, m; cin >> n >> m;
    string s, t, mid, ans="", rev="";
    while(n--) {
        cin >> s;
        t = s; //t复制一下字符串,用来反转
        reverse (all(t));
        if (s == t) mid = t; //如果本身回文就放中间,不用考虑
        else if (st.count(t)) { //查询这个字符串是否有出现
            ans += t; //如果有和反转后相等的,就把反转存前面
            rev = s + rev; //然后这个字符串放后面。
        }
        st.insert(s); //将这个字符串存进集合中
    }
    ans = ans + mid + rev;
    cout << ans.size() << endl << ans << endl;
}

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



九、sort

普通数组的排序方式:

const int N = 1e6 + 10;
int a[N];
void solve() {
	int n;
	sort(a, a + n); //默认将 a数组中 0 ~ n - 1 上的数从小到大排列。
	sort(a + 1, a + n + 1); //将 a数组中 1 ~ n 上的数从小到大排列。

	sort(a, a + n, greater<int>()); //从大到小排列。
	sort(a, a + n, less<int>()); //从小到大排列。
}

vector数组的排序方式:

	int n;
	vector<int> a(n);
	sort(a.begin(), a.end());

结构体的排序方式:

const int N = 1e6 + 10;
struct node {
	int x, y, z;
}a[N];

bool cmp (const node &a, const node &b) {
	if (a.x != b.x) return a.x < b.x;
	if (a.y != b.y) return a.y < b.y;
	return a.z < b.z;
}

void solve() {
	int n;
	sort(a, a + n, cmp);
}



十、pair

介绍:

可以将pair看成是只有两个元素的结构体。


初始化与赋值:

	pair<int, int> p1;
	pair<int, string> p2;
	pair<pair<int, int>, int> p3;
	pair<pair<int, int>, pair<int, int>> p4;

	pair<string, int> p5("qiuyan", 666);

	p1 = {520, 1314};
	p2 = make_pair(123, "mutouren");
	p3 = pair<pair<int, int>, int>(pair<int, int>(1, 2), 3);
	p4.first.first = 1; p4.first.second = 2; p4.second.first = 3; p4.second.second = 4;


访问方式:

	int n;
	pair<int, int> p[n];
	for (int i = 0; i < n; i++) {
		cin >> p[i].first >> p[i].second;
		cout << p[i].first << " " << p[i].second << endl;
	}



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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值