目录
2.8.unordered_set和unordered_map
3.6.2.2.1. insert(Index,count,ch)
3.6.2.2.3.insert(index,str,index_str,count)
3.6.2.2.4.insert(index,s,count)
1.C++入门
1.1.常用头文件
#include<bits/stdc+.h>
//俗称万能头。优点:包含常用的所有头文件,在算法竞赛中推荐使用,因为不会考虑编译时间。缺点:由于包含了很多头文件,在同时使用using namespace std;的情况下,部分变量名不能使用,因为与std::Xxx重名。
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<queue>
#include<deque>
#incude<stack>
#include<iterator>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
1.2.C++命名空间
question:为什么会有命名空间?
answer:简单来说,为了在复杂工程中避免重名问题,命名空间将标识符进行了本地化,不会与头文件中全局变量的名字进行冲突。
算法竞赛中,通常不会使用命名空间相关知识,但有时会大量使用标佳库内容,所以通常加上using namespace std;
#include<bits/stdc++.h>
int main() {
int a, b;
std::cin >> a >> b;
std::cout << a << " " << b << endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
cout << a << " " << b << endl;
return 0;
}
1.3.输入输出
C++的输入与输入是通过流(stream)来实现的(通过命名空间std,如果没有using namespace std,输入输出换行需要写在前加上std::),我们常用的是预定义标准流对象中的std::cin与std::cout以及std:.endl;
1.3.1.输入std::cin(cin)
#include<bits/stdc++.h>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
return 0;
}
1.3.2.输出std::cout(cout)
#include<bits/stdc++.h>
using namespace std;
int main() {
int a = 0;
cout << a;
return 0;
}
1.3.3.换行std::endl(endl)
#include<bits/stdc++.h>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
cout << a << " " << b << endl;
return 0;
}
优点:相比于scanf与printf。std::cin与std::cout无需指定类型即可输入与输出。
缺点:1.由于兼顾c语言的输入输出方式,在不使用解绑的情况下,速度比scanf与printf 相比会慢上许多。(解绑函数:ios::sync_with_stdio(false);cin.tie(0);cout.tie(O))2.在一些题目中要求特殊格式的输出时,cout不比printf方便。
1.4.C++结构体
C++的结构体与c语言中的结构体形式基本一样,区别在于C++对结构体进行了拓展,其功能基本等同于C++的类(Class),因此,我们可以重载结构体的运算符,在结构体内编写函数,可以直接用结构体名声明一个新的结构体变量,这些在以前的c语言是无法做到的。
1.4.1.结构体声明
struct node {
int a, b;
};
//声明结构体变量
struct node x;
node y;//C语言只能使用上一个方法
1.4.2.结构体重载
我们可以通过多种形式重载结构体内的运算符,下面给出推荐的一种形式
struct node
{
int u, v, w;
bool operator < (const node &x)const{
return w > x.w;//也可以换成this->w > x.w;
}
};
其代表的意思是,如果当前结构体的w>结构体x的w,那么则当前结构体<结构体x。
重载结构体的运算符可以在需要实现结构体排序,或在作为一些STL容器的变量类型时非常有用。
1.4.3.结构体函数
1.4.3.1构造函数
1.4.3.1.1.参数全初始化
this指针用来指出第一个val是结构体的成员变量val,this可以不写,写了更清晰
struct Node {
int val;
Node* next;
//默认构造函数 Node(){} C++自带,我们不需要显式地写出来
Node(int val,Node* p){
this->val = val;
this->next = p;
}
};
/*struct Node {
int val;
Node* next;
Node(int v,Node* p){
val = v;
next = p;
}
};*/
1.4.3.1.2.参数部分初始化
#include<iostream>
using namespace std;
struct Node {
int val;
Node* next;
Node(int v) {
val = v;
}
};
int main() {
Node head(10);
Node node = { 2 };
(&head)->next = &node;
cout << head.val << endl << (&head)->next->val << endl << node.val << endl;
return 0;
}
/*运行结果:
10
2
2*/
[注] 如果构造函数只写了部分参数,在实例化时会比较麻烦,一是实例化时不能不带参数,也不能用成员列表把所有参数都写上去,只能写你在构造函数时限定的部分参数。
1.4.3.1.3.简便写法
#include<iostream>
using namespace std;
struct Node {
int val;
Node* next;
//默认构造函数
/*Node() {}*/
Node() :val(), next() {}
/*Node(int v) {
val = v;
}*/
Node(int val) :val(val) {}
};
int main()
{
Node head(10);
Node node = { 2 };
(&head)->next = &node;
cout << head.val << endl << (&head)->next->val << endl << node.val << endl;
return 0;
}
/*运行结果:
10
2
2*/
1.4.3.2.结构体中的其他函数
#include<iostream>
using namespace std;
struct Node {
int val;
Node* next;
/*Node() {}*/
Node(int val) :val(val) {}
int add(int a, Node* x) {
return a + x->val;
}
};
int main()
{
Node head(10);
Node node = { 2 };
(&head)->next = &node;
cout << head.add(head.val, (&head)->next);
return 0;
}
//输出结果12
2.STL
STL即标准模板库(Standard Template Library) ,是C++标准库的一部分,里面包含了一些模板化的通用的数据结构和算法。由于其模板化的特点,它能够兼容自定义的数据类型,避免大量的造轮子工作。NOI和ICPC赛事都支持STL库的使用,因此合理利用STL可以避免编写无用算法,并且充分利用编译器对模板库优化提高效率。
2.1.容器
顺序容器:
array(C++11):静态的连续数组;
vector:动态的连续数组;
deque:双端队列;
forward_list(C++11):单链表;
list:双链表。
关联容器(默认升序):
set:唯一键的集合,按键排序;
map:键值对的集合,按键排序,键是唯一的;
multiset:键的集合,按键排序;
multimap:键值对的集合,按键排序。
无序关联容器:
unordered_set:唯一键的集合,按键生成散列;
unordered_map:键值对的集合,按键生成散列,键是唯一的;
unordered_multiset:键的集合,按键生成散列;
unordered_multimap:键值对的集合,按键生成散列。
容器适配器:
stack:适配一个容器以提供栈(LIFO数据结构);
queue:适配一个容器以提供队列(LIFO数据结构);
priority_queue:适配一个容器以提供优先级队列。
2.2.迭代器
迭代器是一种广义化的指针,它使得C++程序可以通过统一的方式处理不同的数据结构(例如容器和范围(C++20起)。迭代器库提供了迭代器的定义,同时还提供了迭代器特征、适配器及相关的工具函数。因为迭代器是指针的抽象,所以它们的语义是C++的指针的大多数语义的泛化。这确保指针能够用于所有接受迭代器的函数模板。(理解为指针)
#include<bits/stdc++.h>
using namespace std;
int main() {
vector<int> v;
v.push_back(2);//v:{2}
v.push_back(0);//v:{2, 0}
v.push_back(1);//v:{2, 0, 1}
vector<int>::iterator it;//迭代器的声明
for (it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}//v.end()是v容器最后一个元素的再后一个
return 0;
}
/*运行结果:
2
0
1*/
2.3.vector
2.3.1.功能
vector容器的功能和数组非常相似,使用时可以把它看成一个数组
2.3.2.vector和普通数组的区别:
1.数组是静态的,长度不可改变,而vector可以动态扩展,增加长度
2.数组内数据通常存储在栈上,而vector中数据存储在堆上
2.3.3.动态扩展
动态扩展并不是在原空间之后续接新空间,而是找到比原来更大的内存空间,将原数据拷贝到新空间,释放原空间
2.3.4.vector初始化
1.vector<数据类型> 名;
2.vector<数据类型> 名(大小);
3.vector<数据类型> 名(大小,初始值);
vector容器大小:
resize(int num)//重新指定容器的长度为num,若容器变长,则以默认值0填充新位置,如果容器变短,则末尾超过容器长度的元素被删除
vector<int> x;
//使用reszie
x.resize(5);
vector预留空间
reserve(int len);
//容器预留len个元素长度,也就是把容量扩为len,预留的位置并不初始化,同时也不可访问
vector<int> x;
//使用reserve
x.reserve(5);
2.3.5.常用函数
1.push_back(elm); //尾部插入元素elm
2.pop_back(); //删除最后一个元素
3.insert(const_iterator pos,ele); //在迭代器指向的位置pos前插入一个元素ele
4.insert(const_iterator pos,int count,ele); //在迭代器指向的位置pos处插入count个元素ele
5.erase(const_iterator pos); //删除迭代器指向的元素
6.erase(const_iterator begin,const_iterator end); //删除迭代器区间[begin,end)之间的元素
7.clear(); //删除容器中所有元素
8.front(); //返回容器中第一个元素
9.back(); //返回容器中最后一个元素
10.size(); //返回容器的大小,即容器中元素的个数
11.empty(); //判断容器是否为空,为空返回1,否则返回0
12.swap(v); //当前容器与容器v交换,同时v被清空
13.begin(); //正向起始迭代器
14.end(); //正向终点迭代器,指向的是最后一个元素的后面
14.rend(); //反向终点迭代器
14.rbegin(); //反向起始迭代器
#include<bits/stdc++.h>
using namespace std;
int main() {
vector<int> v;
v.push_back(2);//v:{2}
v.push_back(0);//v:{2, 0}
v.push_back(1);//v:{2, 0, 1}
/*push_back(int n)实际上是在v.end()处插入元素n
如果vector<int> v(2,0);//初始大小为2,值为0
v.push_back(2);//v:{0, 0, 2}
v.push_back(0);//v:{0, 0, 2, 0}
v.push_back(1);//v:{0, 0, 2, 0, 1}*/
v.pop_back();//v:{2, 0}
v.insert(v.begin(), 5);//v:{5 ,2, 0}
v.insert(v.begin(), 2, 5);//v:{5, 5, 5 ,2, 0}
v.erase(v.begin());//v:{5, 5 ,2, 0}
v.erase(v.begin(), v.end() - 2);//v:{2, 0}
int t = v.front();//t = 2
int o = v.back();//o = 0
int x = v.size();//x = 2
int f;
f = v.empty();//不为空 f = 0
v.clear();//v:{}
vector<int> s;
s.push_back(5);//s:{5}
s.push_back(4);//s:{5, 4}
s.push_back(3);//s:{5, 4, 3}
v.swap(s);//v:{5, 4, 3},s:{}
f = v.empty();//为空 f = 1
return 0;
}
2.3.6.重载符号
=号,vector<int>v1,v2; v1 = v2//让v2赋值给v1
==号,v1==v2,按照字典序判断v1是否与v2相等,不相等就为!=
>号,v1>v2,按照字典序判断v1是否大于v2,位数不够补零
<号,v1>v2,按照字典序判断v1是否小于v2,位数不够补零
2.4.array
array即是数组,可以认为是对c语言中静态数组的封装其声明形式为array<数据类型,长度> name;
算法中不常用array,不做解释。
2.5.set
set是关联容器,含有Key类型对象的已排序集。用比较函数比较(Compare)进行排序。搜索、移除和插入拥有对数复杂度。set通常以红黑树实现。
这句话是指,set中的元素唯一,并且是有序状态,初始默认升序,搜索,删除,插入复杂度均为O(logn);
2.5.1.set初始化
set<数据类型> 名;//默认升序
set<type,greater<数据类型> > 名;//降序
set<type,less<数据类型> > 名;升序
2.5.2.常用函数
set<type> s;
s.insert(val);//插入一个值→set.insert(begin,end);插入另一块容器区间[begin,end)的值。
s.count(val);/查询set中值为val的数有多少,返回o和1。
s.erase(val);//删除值为val的元素
s.erase(it);//删除迭代器it指向元素
s.erase(begin,end);//删除迭代器区间[begin,end)的元素
#include<bits/stdc++.h>
using namespace std;
int main() {
set<int> s;
s.insert(5);//s:5
s.insert(1);//s:1 5
s.insert(7);//s:1 5 7
s.insert(3);//s:1 3 5 7
s.insert(6);//s:1 3 5 6 7
s.insert(5);//重复的元素不会插入,set的键值唯一,s:1 3 5 6 7
for (auto a : s) {
cout << a << ' ';
}
cout << endl;
cout << s.count(1) << endl;//输出1
cout << s.count(0) << endl;//输出0
s.erase(5);//s:1 3 6 7
for (auto a : s) {
cout << a << ' ';
}
cout << endl;
s.erase(s.begin());//s:3 6 7
for (auto a : s) {
cout << a << ' ';
}
cout << endl;
s.erase(s.begin(), --s.end());//s:7
for (auto a : s) {
cout << a << ' ';
}
cout << endl;
return 0;
}
/*运行结果:
1 3 5 6 7
1
0
1 3 6 7
3 6 7
7*/
2.6.map
std::map是有序键值对容器,它的元素的键是唯一的。用比较函数Compare排序键。搜索、移除和插入操作拥有对数复杂度。map 通常实现为红黑树.
2.6.1.map初始化
map<数据类型1,数据类型2> 名;数据类型1为键值key,数据类型2为映射val
2.6.2.map访问
2.6.2.1.下标
和普通数组一样,例如一个定义为map<char, int> mp的map来说,可以直接使用mp['c']的方式来访问它对应的int整数。可以直接使用mp['c'] = 20这样的方式来赋值
map<char, int> mp;
mp['c'] = 20;
cout << mp['c']; //答案输出20
但是要注意的是,map中的键是唯一的,例如以下代码:
map<char, int> mp;
mp['c'] = 20;
mp['c'] = 30; //30覆盖了20
mp['c'] = 666; //666覆盖了30
cout << mp['c']; //答案输出666
2.6.2.2.迭代器
map迭代器的定义和其他STL容器迭代器定义的方式相同;map会以键从小到大的顺序自动排序
#include<bits/stdc++.h>
using namespace std;
int main() {
map<char, int> mp;
mp['c'] = 222;
mp['a'] = 333;
mp['b'] = 444;
for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " " << it->second << endl;
}
return 0;
}
/*运行结果:
a 333
b 444
c 222*/
2.6.3.常用函数
2.6.3.1.find()
find(key);//返回键为key映射的迭代器,时间复杂度为O(logN),N为map中映射的个数
#include<bits/stdc++.h>
using namespace std;
int main() {
map<char, int> mp;
mp['a'] = 222;
mp['b'] = 333;
mp['c'] = 444;
map<char, int>::iterator it = mp.find('b');
cout << it->first << " " << it->second;
return 0;
}
/*运行结果:
b 333*/
2.6.3.2.erase()
2.6.3.2.1. 删除单个元素
mp.erase(it),it为需要删除的元素的迭代器。时间复杂度为O(1)
mp.erase(key),key为要删除的映射的键。时间复杂度O(logN),N为map内元素的个数:
#include<bits/stdc++.h>
using namespace std;
int main() {
map<char, int> mp;
mp['a'] = 222;
mp['b'] = 333;
mp['c'] = 444;
map<char, int>::iterator it = mp.find('b');
mp.erase(it);
for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " " << it->second << endl;
}
cout << endl;
mp.erase('c');
for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " " << it->second << endl;
}
return 0;
}
/*运行结果:
a 222
c 444
a 222*/
2.6.3.2.2.删除一个区间内所有的元素
mp.erase(first, last),其中,first为需要删除的区间的起始迭代器,last为需要删除的区间末尾迭代器的下一个地址,即为
删除左闭右开的区间[first, last)。时间复杂度为O(last - first)
#include<bits/stdc++.h>
using namespace std;
int main() {
map<char, int> mp;
mp['a'] = 222;
mp['b'] = 333;
mp['c'] = 444;
map<char, int>::iterator it = mp.find('b');
mp.erase(it, mp.end()); //删除it之后的所有映射,即b 333和 c 444
for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " " << it->second << endl;
}
return 0;
}
/*运行结果:
a 222*/
2.6.3.3.size()和clear()
1.size()用来获得map中映射的对数,复杂度为O(1);
2.clear()用来清空map中的所有元素,复杂度为O(N),N为map中元素个数。
#include<iostream>
#include<map>
using namespace std;
int main(){
map<char, int> mp;
mp['a'] = 222;
mp['b'] = 333;
mp['c'] = 444;
mp.clear(); //清空map
cout << mp.size();
return 0;
}
//运行结果:0
2.7.multiset和multimap
multiset和lmultimap均为key值可以重复的set和map,由于实际用。处很少,再次不做叙述有兴趣可以看multimap与multiset。
2.8.unordered_set和unordered_map
2.8.1.unordered_set
unordered_set是含有Key类型唯─对象集合的关联容器。搜索、插入和移除拥有平均常数时间复杂度。在内部,元素并不以任何特别顺序排序,而是组织进桶中。元素被放进哪个桶完全依赖其值的哈希。这允许对单独元素的快速访问,因为哈希一旦确定,就准确指代元素被放入的桶。
2.8.2.unordered_map
unordered_map是关联容器,含有带唯一键的键-值pair。搜索、插入和元素移除拥有平均常数时间复杂度。元素在内部不以任何特定顺序排序,而是组织进桶中。元素放·进哪个桶完全依赖于其键的哈希。这允许对单独元素的快速访问,因为一旦计算哈希,则它准确指代元素所放进的桶。
2.9.stack和queue
2.9.1.stack
栈是基本的数据结构之一,特点是先进后出,就如开进死胡同的车队,先进去的只能最后出来.
2.9.1.1.声明
stack<数据类型> 名;
2.9.1.2.常用操作
q.push(x); //将x压入栈顶
q.top(); //返回栈顶的元素
q.pop(); //删除栈顶的元素
q.size(); //返回栈中元素的个数
q.empty(); //检查栈是否为空,若为空返回true,否则返回false
#include<bits/stdc++.h>
using namespace std;
int main() {
stack<int> q;
q.push(5);//q:5 栈顶5
q.push(4);//q:5 4 栈顶4
q.push(3);//q:5 4 3 栈顶3
q.push(2);//q:5 4 3 2 栈顶2
q.push(1);//q:5 4 3 2 1 栈顶1
cout << "top:" << q.top() << endl;
q.pop();//q:5 4 3 2 栈顶2
cout << "size:" << q.size() << endl;
cout << q.empty();
return 0;
}
/*运行结果:
top:1
size:4
0*/
2.9.2.queue
2.9.2.1.声明
queue<数据类型> 名;
2.9.2.2.常用函数
q.push(x); //将x放入队列
q.front(); //返回队首元素
q.pop(); //删除队首元素
q.back(); //返回队尾元素
q.size(); //返回q中的元素个数
q.empty(); //检查队列是否为空,若为空返回true,否则返回false
#include<bits/stdc++.h>
using namespace std;
int main() {
queue<int> q;
q.push(5);//q:5 队首5 队尾5
q.push(4);//q:5 4 队首5 队尾4
q.push(3);//q:5 4 3 队首5 队尾3
q.push(2);//q:5 4 3 2 队首5 队尾2
q.push(1);//q:5 4 3 2 1 队首5 队尾1
cout << "front:" << q.front() << endl;
cout << "back:" << q.back() << endl;
q.pop();//q:4 3 2 1 队首4 队尾1
cout << "front:" << q.front() << endl;
cout << "back:" << q.back() << endl;
cout << "size:" << q.size() << endl;
cout << q.empty();
return 0;
}
/*运行结果:
front:5
back:1
front:4
back:1
size:4
0*/
2.10.priority_queue
2.10.1.声明
priority_queue<数据类型> 名; //默认大到小排序
priority_queue<数据类型,vector<数据类型>,greater<数据类型> > 名; //小到大排序
若数据类型为结构体则需要定义结构体如下:
struct 结构体名
{
int x,y;
bool operator < (const 结构体名 &a) const
{
return x<a.x;//按x的值降序序排列
}
};
2.10.2.常用操作
#include<bits/stdc++.h>
using namespace std;
int main() {
priority_queue<int> a; //默认大到小
priority_queue<int, vector<int>, greater<int> > b; //小到大
a.push(3);//a:3 队首 3 队尾 3;
a.push(2);//a:3 2 队首 3 队尾 2
a.push(1);//a:3 2 1 队首 3 队尾 1
a.push(9);//a:9 3 2 1 队首 9 队尾 1
b.push(3);//b:3 队首 3 队尾 3
b.push(2);//b:2 3 队首 2 队尾 3
b.push(1);//b:1 2 3 队首 1 队尾 3
b.push(9);//b:1 2 3 9 队首 3 队尾 9
while (!a.empty()) {
cout << a.top() << " ";
a.pop();
}
cout << endl;
while (!b.empty()) {
cout << b.top() << " ";
b.pop();
}
return 0;
}
/*运行结果:
9 3 2 1
1 2 3 9*/
3.string类
3.1.初始化
string对象的初始化和普通类型变量的初始化基本相同,只是string作为类,还有类的一些特性:使用构造函数初始化。如下表,第2 4 6条是作为类才有的初始化方式:
#include<bits/stdc++.h>
using namespace std;
int main() {
string s1 = "";//初始化一个空串,将此串转化为string类
string s1;//默认初始化,s1是一个空串,等价于string s1 = "",将此串转化为string类
string s2 = "12345";//初始化一个串"12345",将此串转化为string类
string s2("12345");//等价于string s2 = "12345",将此串转化为string类
string s3 = s2;//将s2传给s3,将此串转化为string类
string s3(s2);//等价于string s3 = s2,将此串转化为string类
string s4 = string("value");//等价于string s4("value"),将此串转化为string类
return 0;
}
3.2.输入输出
3.2.1.用cin输入
和int、double等类型的cin一样使用。需要说明一点:string对象会自动忽略开头的空白(既空格、换行符、制表符等),并从第一个真正的字符开始读入,直到遇到下一处空白(既空格、换行符、制表符等),即不能读入空格,换行符等
string s1;
cin >> s1;
cout << s1 << endl;
3.2.2.用getline读取一整行
getline的函数格式:getline(cin,string对象)
getline的作用是读取一整行,直到遇到换行符才停止读取,期间能读取像空格、Tab等的空白符。实例如下:
string s1;
getline(cin, s1);
cout << s1 << endl;
3.3.比较string的大小
string str = "HelloWorld";
string str1 = "HelloWorld ";
这种情况,尽管两者的前面对应的字符都一样,但是str1(多一个空格),所以str1>str。
string str = "HelloWorld";
string str1 = "HelloWi ";
这种情况比较的是第一个相异字符,根据字符值比较大小,因为i的字符值<o的字符值,所以str1< str。
string str = "HelloWorld";
string str1 = "HelloWorld";
这种情况,两个字符串完全一样,则str==str1。
3.4.字符串的加法
3.4.1.两个string对象相加
#include<bits/stdc++.h>
using namespace std;
int main() {
string s1, s2;
s1 = "abc";
s2 = "def";
s1 += s2;
string s3;
s3 = "abc" + s2 + "gh";
cout << s3 << endl;
cout << s1 << endl;
return 0;
}
/*运行结果:
abcdefgh
abcdef*/
3.4.2.string对象加上一个字符(或字符串)字面值
3.4.2.1.例子
#include<bits/stdc++.h>
using namespace std;
int main() {
string str = "Hello";
string s2 = str + ',';
s2 += ',';//等价于s2 = s2 + ','
str += "abc";//等价于str = str + "abc"
cout << s2 << endl << str << endl;
return 0;
}
/*运行结果:
Hello,,
Helloabc*/
3.4.2.2.易错点
string str = "Hello";
(1)string s2 = str + "," + "world";
(2)string s3 = "Hello" + "," + str;
(1)正确;(2)错误
(2)错误的原因是:当string对象和字符或字符串字面值相加时,必须确保+号的两侧的运算对象至少有一个string类,这样才能返回一个类。"Hello" + "," 返回的不是string类,“Hello”是一个字符串,“,”是一个字符串,字符串之间不能加,string类之间可以加
至于(1),需要明白,str + “,”会返回一个string类。
3.5.获取和处理string中的每个字符
3.5.1.类似于字符数组,使用下标访问[]
获取字符串长度:size()
#include<bits/stdc++.h>
using namespace std;
int main() {
string s = "Hello world!";
cout << s[0] << endl;
cout << s[s.size() - 1] << endl;
cout << s << endl;
s[0] = 'h';
cout << s << endl;
for (int i = 0; i < s.size(); i++) {
cout << s[i] << ' ';
}
}
/*运行结果:
H
!
Hello world!
hello world!
h e l l o w o r l d !*/
3.5.2.使用迭代器访问
#include<bits/stdc++.h>
using namespace std;
int main() {
string s = "Hello world!";
for (auto i = s.begin(); i != s.end(); i++) {
cout << *i << ",";
}
cout << endl;
}
/*运行结果:
H,e,l,l,o, ,w,o,r,l,d,!,*/
3.5.3.使用基于范围的for语句
基于范围的for语句是C++11新提供的一种语句,其语法形式是:
for(auto declaration:expression){
statement;
}
declaration:定义一个变量,它每次的值都是expression中的基础元素
expression:一个已经定义的对象(变量)
statement:具体的语句
#include<bits/stdc++.h>
using namespace std;
int main() {
string s = "Hello world!";
for (auto i : s) {
cout << i << ",";
}
cout << endl;
}
/*运行结果:
H,e,l,l,o, ,w,o,r,l,d,!,*/
3.6.string类型的相关方法
3.6.1.substr成员函数
substr(pos,n)//返回一个string对象,返回的对象包含s从pos下标开始的n个字符。pos和n均为可选参数。pos默认为下标0;n默认为s.size()-pos,pos需满足pos<=s.size().
string s ("value");
string s2 = s.substr();//s2为"value",大小为5
string s3 = s.substr(2);//s3为"lue",大小为3
string s4 = s.substr(5);//s3为"",大小为0
string s5 = s.substr(6);//错误,6大于s.size()
string s6 = s.substr(1,2);//s6为"al",大小为2
string s7 = s.substr(1,7);//s7为"alue",大小为4
string s8 = s.substr(5,7);//s8为"",大小为0
string s9 = s.substr(6,7);//错误,6大于s.size()
3.6.2.string对象的insert()
3.6.2.1.迭代器
1.iterator insert( iterator pos, CharT ch )
2.void insert( iterator pos, size_type count, CharT ch )
3.void insert( iterator pos, InputIt first, InputIt last )
4.插入初始化列表
string s1("value");
s1.insert(s1.begin(), 's');//执行后,s1为"svalue"
s1.insert(s1.begin(), 1, 's');//执行后,s1为"ssvalue"
s1.insert(s1.begin(), s1.begin(), ++s1.begin());//执行后,s1为"sssvalue"
s1.insert(s1.end(), {'1','2'});//执行后,s1为"sssvalue12"
3.6.2.2.下标
3.6.2.2.1. insert(Index,count,ch)
insert( size_type index, size_type count, CharT ch )//在下标index前插入count个字符ch。
string s1("value");
s1.insert(0,2,’s’); //执行后,s1为” ssvalue”
s1.insert(5,2,’s’); //执行后,s1为” valuess”
3.6.2.2.2.insert(index,s)
insert( size_type index, const basic_string& str )//在下标index前插入一个常量字符串或者string对象。
string s1("value");
string s3 = "value";
const char* cp = "value";
s1.insert(0,s3);//执行完后,s1为" valuevalue"
s1.insert(0,cp); //执行完后,s1为" valuevalue"
3.6.2.2.3.insert(index,str,index_str,count)
insert( size_type index, const basic_string& str, size_type index_str, size_type count );//在下标index前插入str中的从str[index_str]开始的count个字符。
string s1("value");
string s3 = "value";
s1.insert(0,s3,1,2);//执行后,s1为” alvalue”
3.6.2.2.4.insert(index,s,count)
insert( size_type index, const CharT* s, size_type count );//在index前插入常量字符串的count个字符。
string s1("value");
const char* cp = "value";
s1.insert(0, cp,3); //执行后,s1为” valvalue”
3.6.3.string对象的erase()
1.basic_string & erase(size_type pos, size_type n);//如果string对象s调用,它删除s从pos下标开始的n个字符,并返回删除后的s。当pos > s.size()时,报错。
2.iterator erase(const_iterator position);//如果string对象s调用,它删除s迭代器position位置的字符,并返回下一个字符的迭代器。
3.iterator erase(const_iterator first, const_iterator last);//如果string对象s调用,它删除s迭代器[first,last)区间的字符,并返回last字符的迭代器。
string s1("value");
string s2("value");
string s3("value");
string s4("value");
s1.erase();//执行后,s1为空
s2.erase(0,2); //执行后,s2为”lue”
s3.erase(s3.begin());//执行后,s3为”alue”
s4.erase(s4.begin(),++s4.begin());//执行后,s4为”alue”
3.6.4.字符串和整形间的转换
stoi(s)//将字符串s转换成整形
to_string(x)//将整形x转换成字符串
#include<bits/stdc++.h>
using namespace std;
int main() {
string s = "123456";
int x = stoi(s);
int y = 654321;
string str = to_string(y);
cout << x << endl << str << endl;
return 0;
}
/*运行结果:
123456
654321*/
3.6.5.搜索函数find()
str1.find(str2);/返回位置起始若失败则返回-1;
#include<bits/stdc++.h>
using namespace std;
int main() {
string str = "There are two needles in this haystack with needles.";
string str2 = "needle";
int found = str.find(str2);//返回第一个"needles"n的下标
cout << found << endl;
string str3 = "123";
found= str.find(str3);//返回第一个"123"的下表.查找失败
cout << found << endl;
return 0;
}
/*运行结果:
14
-1*/
4.STL库常用算法
4.1.快速排序std::sort
1.若对普通数组使用则为sort(array+l,array+I+r,(可选)cmp);代表将数组u.r]区间进行排序,若包含cmp参数则按照cmp规定排序方法,否则默认升序;
2若对STL顺序容器使用则为sort(vec.begin()+l,vec.begin()+l+r,cmp);代表将容器[.r]区间进行排序,若区间刚好为[0,size)则可以使用sort(vec.begin(),vec.end(),cmp);
3 std::sort中提供std:less()与std::greater()分别代表升序和降序。
4.2.二分查找
1.lower_bound与jupper_bound;前者查询第一个大于等于某个数的位置,后者查询第一个大于某个数的位置;
2.对于数组,则为lower_bound(a+l,a+l+r,val);返回指针;
对于顺序容器则为lower_bound(a.begin()+l,a.end()+I+r,val。返回迭代器。与sort相同,如果覆盖区间刚好为[o,size)则可以用begin和lend替代;
3若想获得下标,则对于数组减去数组名,对于迭代器减去begin()即可;
4关联容器中, map和set均重载在了自身的函数中。
4.3.交换std::swap()
注:STL容器中也重载了交换,由于vector的clear并不是真正的清除,可以用swap实现
如vector<int>().swap(a);并且交换两个vector的时间复杂度为o(1);交换静态数组为o(n),std:.array同样也是o(n);
4.4.std::fill()
由于memset(只能赋值按位相同的值,局限性很大。fill(begin,end,val);可以将[begin,end]全部设为val。
5.基于范围的for循环(C++11起)
5.1.理解
auto类型说明符用于自动推导变量等的类型。
例如:auto a = 1;自动推导为int
auto b = a+0.1;自动推导为double
5.2.语法
语法为for (auto 范围变量声明:范围表达式){
循环语句;
}
5.3.使用
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
vector<int> v;
v.push_back(1);
v.push_back(2);
map<char, int> mp;
mp['0'] = 1;
mp['1'] = 2;
set<int> st;
st.insert(1);
st.insert(5);
for (auto x : v) {//推导为int
cout << x << ' ';
}
cout << endl;
cout << endl;
for (auto x : mp) {//推导为迭代器
cout << x.second << " " << x.first << endl;
}
cout << endl;
for (auto x : st) {//推导为迭代器
cout << x << " ";
}
return 0;
}
/*运行结果:
1 2
1 0
2 1
1 5*/
6.复杂度
6.1.时间复杂度
时间复杂度是衡量算法执行效率的重要指标,它描述了算法运行时间随输入规模增长的趋势。通常用大O符号(O)来表示时间复杂度。
在分析算法的时间复杂度时,我们通常关注算法执行所需要的基本操作次数(比如比较次数、赋值次数等),并用输入规模n的函数来表示这些基本操作次数的增长趋势。
常见的时间复杂度包括:
- O(1): 常数时间复杂度,表示算法的执行时间是一个常数,与输入规模无关。
- O(log n): 对数时间复杂度,通常是以2为底的对数,常见于二分查找等算法。
- O(n): 线性时间复杂度,算法执行时间与输入规模成线性关系。
- O(n log n): 线性对数时间复杂度,常见于快速排序、归并排序等分治算法。
- O(n^2): 平方时间复杂度,常见于简单的嵌套循环算法。
- O(2^n): 指数时间复杂度,通常出现在暴力穷举算法中,效率极低。
在实际分析算法的时间复杂度时,我们通常考虑最坏情况下的时间复杂度,因为它能够提供对算法性能的较为悲观但更为全面的估计。
6.2.空间复杂度
空间复杂度是另一个重要的算法性能指标,描述了算法在执行过程中所需的额外空间或内存量。与时间复杂度类似,空间复杂度也使用大O符号(O)来表示。
在分析算法的空间复杂度时,我们通常关注算法在运行过程中所需要的额外空间,不包括输入数据所占用的空间。空间复杂度包括以下几种情况:
- O(1): 常数空间复杂度,表示算法所需的额外空间是一个常数,与输入规模无关。
- O(n): 线性空间复杂度,表示算法所需的额外空间与输入规模成线性关系。
- O(n^2): 平方空间复杂度,表示算法所需的额外空间与输入规模的平方成正比。
- O(log n): 对数空间复杂度,通常是以2为底的对数,与输入规模的对数成正比。
在实际应用中,我们通常更关注算法的时间复杂度,因为空间通常比时间更为宝贵。但是,在一些内存受限的环境下(比如嵌入式系统、移动设备等),空间复杂度也是需要考虑的重要因素。
在分析空间复杂度时,我们通常考虑算法所使用的额外空间的最大值,即算法在任何时刻所占用的最大空间量。