由于本人实力尚浅,接触算法没多久,写这篇blog仅仅是想要提升自己对算法的理解,如果各位读者发现什么错误,恳请指正,希望和大家一起进步。(●’◡’●)
using namespace std;
#include //引入头文件
vector<int> a; //相当于长度动态变化的int数组
vector<int> b[88]; //相当于第一维长88,第二维长度动态变化的int数组(类似于b[88][])
struct rec {};
vector<rec> c; //自定义的结构体;类型也可以保存在vector中
代码 | 含义 | 时间复杂度 |
---|---|---|
c.front() |
返回第一个数据 | O(1) |
c.pop_back() |
删除最后一个元素 | O(1) |
c.push_back() |
在尾部加一个元素 | O(1) |
c.back() |
返回数组中的最后一个元素 | O(1) |
c.size() |
返回实际长度 | O(1) |
c.empty() |
返回一个bool 类型,表面vector 是否为空 |
O(1) |
c.clear() |
把vecto r清空 |
O(N) |
c.begin() |
返回首元素的迭代器(通常来说是地址) | O(1) |
c.end() |
返回最后一个元素后一个位置迭代器(地址) | O(1) |
begin()
函数返回指向vector
中第一个元素的迭代器。*c.begin()
和c[0]
作用相同。c.end()
和c[n]
都是越界访问,使用时要注意。vector
for (int i = 0; i < a.size(); i++) //利用下标遍历
cout << a[i] << endl;
for (vector<int>::iterator p = a.begin(); p != a.end(); p++) //利用指针遍历
cout << *p << endl;
一种先进先出(FIFO)的数据结构。
using namespace std;
#include
queue<int> q;
struct rec {}; queue<rec> q;
方法 | 含义 | 时间复杂度 |
---|---|---|
push |
在尾部压入一个元素 | O(1) |
pop |
删除队头元素(即出队) | O(1) |
front |
取出队头元素 | O(1) |
back |
取出队尾元素 | O(1) |
queue
while (!q.empty())
{
cout << q.front() << endl;
q.pop();
}
就是在正常队列的基础上加了优先级,保证每次的队首元素都是优先级最大的。
它的底层是通过堆来实现的,可以理解为一个大根二叉堆。
#include
priority_queue<int> q;
方法 | 含义 | 时间复杂度 |
---|---|---|
push |
把元素插入堆 | O ( l o g n ) O(log_n) O(logn) |
pop |
删除堆顶元素 | O ( l o g n ) O(log_n) O(logn) |
top |
取到堆顶元素 | O(1) |
queue
和priority_queue
取到队头(堆顶)元素的方法不一样。priority_queue
也有size
和empty()
,priority_queue<int, vector<int>, greater<int>> heap; //小根堆,每次取出都是队列中最小的元素
参加解释:
vector
是用来承载底层数据结构堆的容器,若优先队列中存放的是double型数据,就要填vector< double >
greater
表示数字小的优先级大,堆顶为最小的数字。less
则与之相反。struct cmp1 {
bool operator()(int x, int y) {
return x > y;
}
};
struct cmp2 {
bool operator()(const int x, const int y) {
return x < y;
}
};
priority_queue<int, vector<int>, cmp1> q1; // 小根堆
priority_queue<int, vector<int>, cmp2> q2; // 大根堆
关于设置优先级我在这里就不在赘述了,因为打算法比赛的话基本上掌握最基础的就完全够用了
想要了解更多的—>click here
双端队列是一个支持在两端高效插入或者删除元素的连续线性存储空间。
它就像是vector
和queue
的结合。
#include
deque<int> dq;
方法 | 含义 | 时间复杂度 |
---|---|---|
begin()/end() |
deque 的头/尾迭代器(地址) |
O(1) |
front()/back() |
队头/队尾元素 | O(1) |
push_back()/push_front() |
将元素插入队尾/队头 | O(1) |
pop_back()/pop_front() |
删除队尾/队头元素 | O(1) |
[] |
随机访问 | O(1) |
set
容器里的元素不会重复(multiset
可以包含若干个相同的元素),当插入集合中已有的元素时,并不会插入进去,而且set容器里的元素自动从小到大排序。内部实现是一颗红黑树(平衡树的一种)
总的来说:set里面的元素不重复 且有序
#include
set<int> s;
struct rec {}; set<rec> s;
方法 | 含义 | 时间复杂度 |
---|---|---|
size()/empty()/clear() |
分别为元素个数,是否为空,清空 | O(1) |
find() |
查找某一元素,有则返回该元素对应的迭代器,无则返回结束迭代器 | |
count() |
查找set 中某一元素出现的次数,由于set 元素唯一,相当于查询元素是否出现 |
|
begin()/end() |
和前面的容器的一样 | O(1) |
由于
set
在算法比赛中使用的频次非常低(仅仅是根据我的个人理解,不对勿喷),所以这里我就只是简单介绍一下set
,不做赘述。
map
容器是一个键值对key-value
的映射(映射类似于函数的对应关系,每一个x
对应一个y
),map
中每个键对应一个值,和python
中的字典非常相似。
//头文件
#include
//初始化定义
map<string, string> mp;
map<string, int> mp;
map<int, node> mp;//node是结构体类型
方法 | 含义 |
---|---|
size()/empty)/begin()/end() |
和前面的容器一样 |
insert() |
插入元素,要以键值对形式插入 |
[] |
[] 里面是key ,返回的是value |
find() |
返回键为key的映射的二元组的迭代器 O ( l o g N ) O(log_N) O(logN)注意:用find函数来定位数据出现位置,它返回一个迭代器。当数据存在时,返回数据所在位置的迭代器,数据不存在时,返回mp.end() |
mp.find()
,mp.count()
,mp[]key]
均可以查找元素是否存在。[]
操作符是map
中最吸引人的地方,我们可以通过mp[key]
来得到key
对应的value
,还可以对mp[key]
进行赋值操作,改变对应key
对应的value
key
不存在时,mp
会自动创建一个二元组(key,zero),返回zero
的引用。这里的zero
表示一个广义的“零值”,如整数0,空字符串等。如果查找之后不对其赋值,长时间下来就产生许多无用的零值二元组,白白占用空间降低程序运行效率,建议使用[]
前先用find
查询一下。map
特性:map
会按照键的顺序从小到大自动排序,键的类型必须可以比较大小unordered_map
,内部用哈希表实现,内部元素杂乱无章,一种先进后出(FILO)的数据结构
//头文件需要添加
#include
//声明
stack<int> s;
stack<string> s;
stack<node> s;//node是结构体类型
方法 | 含义 | 时间复杂度 |
---|---|---|
empty()/size() |
和前面的容器一样 | O(1) |
top() |
取得栈顶元素(但是不删除) | O(1) |
push() |
将元素入栈 | O(1) |
pop() |
移除栈顶元素 | O(1) |
top
模拟指向栈顶的指针。tt
和hh
来模拟,pair只含有两个元素,可以看作是只有两个元素的结构体。
pair<int, int> a;
pair<int, string> b;
map
的键值对进行插入用p.first
访问第一个元素,p.second
访问第二个元素
//定义结构体数组
pair<int,int> p[20];
for(int i = 0; i < 20; i++) {
//和结构体类似,first代表第一个元素,second代表第二个元素
cout << p[i].first << " " << p[i].second;
}
string是一个字符串类,和char型字符串类似。和char[N]
非常相似
//头文件
#include
//1.
string str1; //生成空字符串
//2.
string str2("123456789"); //生成"1234456789"的复制品
//3.
string str3("12345", 0, 3);//结果为"123" ,从0位置开始,长度为3
//4.
string str4("123456", 5); //结果为"12345" ,长度为5
//5.
string str5(5, '2'); //结果为"22222" ,构造5个字符'2'连接而成的字符串
//6.
string str6(str2, 2); //结果为"3456789",截取第三个元素(2对应第三位)到最后
string s;
cin >> s;
string s;
getline(cin, s);
注意:getline(cin,s)
会获取前一个输入的换行符,需要在前面添加读取换行符的语句,如getchar()
,下面是示例
int n;
string s;
cin >> n;
getchar(); //cin.get()
getline(cin, s);//可正确读入下一行的输入
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
为什么要进行
cin
和cout
解锁
在一些题目中,读入的数据量很大,往往超过了1e5(105)的数据量,而cin
和cout
的读入输出的速度很慢(是因为cin
和cout
为了兼容C语言的读入输出在性能上做了妥协),远不如scanf
和printf
的速度,具体原因可以搜索相关的博客进行了解。
所以对cin
和cout
进行解锁使cin
和cout
的速度几乎接近scanf
和printf
,避免输入输出超时。
注意:cin
cou
t解锁使用时,不能与scanf
,getchar
,printf
,cin.getline()
混用,一定要注意,会出错。
感谢各位童鞋看到这里,这篇blog我还没有写完,几天后还会更新STL函数,赶紧关注一波后面我会持续更新数据结构与算法,也希望大家点点赞,我们一起进步(❁´◡`❁)