C++学习-数据结构-STL与基本数据结构
STL与基本数据结构
第一日集训,笔记。
0. iterator迭代器
0.1 迭代器与指针
迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,它和数组类似,但并不相同,所以先理清楚两者之间的关系:
迭代器:
- 迭代器它模拟了指针的一些功能,通过重载 ‘—>’、'*'、 ‘++’、‘- -’(反向迭代器) 这些操作符(还有其他的一些操作符),封装了指针,提供了比指针更强大的功能,可以看作是智能指针。
- 迭代器与指针还有一个较大区别就是迭代器返回的是对象的引用,而不是对象的值,所以cout只能输出迭代器使用 * 取对象值后的值,而不能直接输出其自身。
指针:
- 指针可以指向函数,迭代器就不可以,迭代器只能指向容器。
- 指针是迭代器的一种,只能用于某些特定的容器,比如数组、结构体。
- 迭代器是指针的抽象和泛化,但是二者还是有很大差别的。
资料来源于 https://www.cnblogs.com/buanxu/p/12756125.html
0.2 迭代器的定义
迭代器的定义方法有以下四种:
正向迭代器: 容器类名::iterator 迭代器名;
常量迭代器: 容器类名::const_iterator 迭代器名;
反向迭代器: 容器类名::reverse_iterator 迭代器名;
常量方向迭代器: 容器类名::const_reverse_iterator 迭代器名;
技巧:自动类型: auto 迭代器名;
(正向)
0.3 迭代器的用法示例
- 无法通过常量迭代器改变迭代器指向的元素。
- 迭代器都可以进行
++
操作,反向迭代器和正向迭代器的区别在于:
i. 对正向迭代器进行++
操作时,迭代器会指向容器中的后一个元素。
ii. 而对反向迭代器进行++
操作时,迭代器会指向容器中的前一个元素。
以下演示如何通过一个迭代器遍历一个vector容器的所有元素:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> arr={1,2,3,4,5};
vector<int>::iterator i;
for(i=arr.begin();i!=arr.end();++i)//begin成员函数返回一个当前vector容器中起始元素的迭代器,end成员函数返回一个当前vector容器中末尾元素后面一个位置的迭代器,
cout<<*i;
return 0;
}
0.3 迭代器功能分类
常用的迭代器按功能强弱分为输入、输出、正向、双向、随机访问五种,这里只介绍常用的三种。
- 正向迭代器。支持以下操作:
++p,p++,*p
。此外,两个正向迭代器可以互相赋值,还可以用==
和!=
运算符进行比较。 - 双向迭代器。双向迭代器具有正向迭代器的全部功能。除此之外,
--p
和p--
都是有定义的。--p
使得 p 朝和++p
相反的方向移动。 - 随机访问迭代器。随机访问迭代器具有双向迭代器的全部功能。若 i 是一个整型变量或常量,则 p 还支持以下操作:
- p+=i:使得 p 往后移动 i 个元素。
- p-i:返回 p 前面第 i 个元素的迭代器。
- p[i]:返回 p 后面第 i 个元素的引用。
- 两个随机访问迭代器 p1、p2 可以用
<、>、<=、>=
运算符进行比较 - 对于两个随机访问迭代器 p1、p2,表达式
p2-p1
也是有定义的,其返回值是 p2 所指向元素和 p1 所指向元素的序号之差
容器 | 迭代器功能 |
---|---|
vector | 随机访问 |
deque | 随机访问 |
list | 双向 |
set / multiset | 双向 |
map / multimap | 双向 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
priority_queue | 不支持迭代器 |
资料来源:http://c.biancheng.net/view/338.html | |
其中的内容更加详细。 |
1.动态数组-vector
vector是STL的动态数组:
- 内存空间连续,索引可以在常数时间内完成。
- 插入删除操作效率低
1.定义
定义int型vector:
vector<int> a;
vector<int> a(100); // a 有 100 个值为 0 的元素
vector<int> a(100,6); //100个值为6的元素
定义string型vector
vector<string> a(10,"hello");
vector<string> a(b.begin(),b.end());// a 是 b 的复制
也可以嵌套定义
vector<vector<int>> a;//可以当做动态二维数组
vector<string> a;
当然还可以和pair,tuper嵌套,不多赘述,本人也没那个水平。
pair可以在这里学一下:pair—模板类型 使用方法
定义二维数组
vector<int> a[MAXN];//第一位大小必须是固定的,第二维是动态的。
2.常用操作
功能 | 例子 | 说明 |
---|---|---|
返回第一个元素的迭代器 | a.begin() | |
返回最后一个元素下一个位置的迭代器 | a.end() | |
元素个数 | a.size(); | 返回元素个数 |
是否为空 | a.empty(); | 空返回0,非空返回1 |
调整大小 | a.resize(n); | 数组大小变为n |
清空 | a.clear(); | 清空 |
中间插入 | a.insert(a.begin()+i,k); | 在第i+1个元素前插入k |
尾部插入 | a.push_back(8); | 尾部插入8 |
中间删除 | a.erase(a.begin()+2); | 删除第3个元素 |
尾部删除 | a.pop_back(); | 删除末尾元素 |
区间删除 | a.erase(a.begin()+i,a.begin()+j); | 删除第i+1到第j个元素 |
翻转 | reverse(a.begin(),a.end()); | 字面意思 |
swap() | 交换两个相同类型的vector |
- 可以用下标访问
- 两个同类型的vector可以用
==,!=,>=,>,<=,<
进行比较,规则和字符串一致。 - 两个同类型的vector(断句)的名字可以用
=
进行赋值 - vector不能直接对头部进行操作
3.链表-list
和vector相反:
- 不连续,随机访问慢
- 插入删除效率高
3.1 定义
和vector基本一致:
定义int型list:
list<int> a;
list<int> a(100); // a 有 100 个值为 0 的元素
list<int> a(100,6); //100个值为6的元素
定义string型list
list<string> a(10,"hello");
list<string> a(b.begin(),b.end());// a 是 b 的复制
也可以嵌套定义
list<list<int>> a;
list<string> a;
3.2 常用操作
链表除了可以进行以上列举的关于vector的全部操作,还可以对头部进行插入删除,以及remove函数
功能 | 例子 |
---|---|
头部插入 | a.push_front(); |
头部删除 | a.pop_back(); |
删除容器中和k相同的值 | a.remove(k); |
- 不可以用下标访问
- 两个相同类型的list可以用
==,!=,>=,>,<=,<
进行比较,规则和字符串一致。 - 两个同类型的list(断句)的名字可以用
=
进行赋值 - 注意list的迭代器为双向迭代器,不能加或减一个整数
3 栈-stack
特点:先进后出,类比一盒泡腾片,最先放进去的元素在最后面,也是最后才能拿出来
由于stack不支持迭代器,所以它的操作会相对简单。
3.1 定义
如果定义一个int型的stack: stack<int> sta;
3.2 常用操作
例子 | 作用 |
---|---|
swap() | 交换两个相同类型的stack |
s.push(k); | 把元素 k 放入栈顶 |
s.top(); | 返回栈顶的元素,但不删除 |
s.pop(); | 删除栈顶的元素,但不会返回 |
s.size(); | 返回栈中的元素个数 |
s.empty(); | 检查栈是否为空 |
- 两个相同的stack可以用
>,==,<=
比较大小(不建议,因为不按规则,可能是倒着比较?) - 两个相同类型的stack(断句)的名字可以用
=
进行赋值 - 不支持下标访问
4 队列-queue
特点:先进先出,和stack相反。
由于queue同样不支持迭代器,操作也会简单些。
4.1 定义
queue<int> que;
4.2 常用操作
例子 | 作用 |
---|---|
q.push(k); | 把元素 k 放入队列 |
q.front(); | 返回队首元素,但不会删除 |
q.pop(); | 删除队首元素 |
q.back() | 返回队尾元素(注意栈不能使用back) |
q.size() | 返回元素个数 |
q.empty() | 检查队列是否为空 |
其他操作同stack。 |
4.3 优先队列(不详细介绍了,以后我专门学的时候回补上 补上了!!!)
以下资料来源自 https://blog.csdn.net/weixin_36888577/article/details/79937886
既然是队列那么先要包含头文件#include
优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
4.3.1 定义
priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆
一般是:
//默认降序
priority_queue <int> q;
//升序队列
这里一定要有空格,不然成了右移运算符↓
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
2.常用操作
和队列基本操作相同:
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- pop 弹出队头元素
- swap 交换内容
5 集合-set
set的每个元素都只能出现一次,并且是排序好的。即去重且排序好。内置二凤查找,访问元素的时间复杂度为O(nlogn),非常高效。
5.1 定义
如果定义一个int型: set<int> A
可以这么初始化: set<int> A={元素1,元素2,......}
5.2 常用操作
例子 | 作用 |
---|---|
swap() | 略 |
A.begin()/A.end() | 略 |
A.insert(k) | 把元素k放入set |
A.erase(k) | 删除元素 k |
A.clear() | 清空set |
A.empty | 判断是否为空 |
A.size() | 返回元素个数 |
A.find(k) | 返回一个指向键值k的迭代器 |
A.lower_bound(k) | 返回一个迭代器。指向键值大于等于k的第一个元素 |
A.upper_bound(k) | 返回一个迭代器,指向键值大于k的第一个元素 |
- 不支持下标访问
- 迭代器为双向
- 可以 =
5.3 multiset
一种不去重且排序的集合,与以上set操作的区别如下
用例 | 作用 |
---|---|
A.earse(k) | 删除所有k |
A.find(k) | 找到指向第一个k的迭代器 |
lower_bound(k)和upper_bound(k)同理 |
6 图map
图是关联容器,它是键(key)到值(value)的映射。同set,排序去重,不过可以支持下标访问(用键值进行值的访问)
6.1 定义
如果定义一个int型: map<int,string> A;
int为key的类型,string为value的类型
可以这么初始化: set<int> A={{key1,value1},{key2,value2},......}
一般而言,使用map的时候直接采取map<typename A, typename B>
的形式即可,map的内部实现默认使用A类型变量的升序来排序map的值
6.2 常用操作
例子 | 作用 |
---|---|
A.begin()/A.end() | 略 |
A.clear() | 略 |
A.empty() | 略 |
A.size() | 略 |
swap() | 略 |
A.erase(k) | 删除键值为k的元素 |
A.find(key) | 返回key(键值)所在位置的迭代器,不能找value |
A[key1]=value1 | 插入一个键值为key1,值为value1的元素 |
A.insert( {key1,value1} ) | 同上,区别是如果key1存在,这个是不能插入,上面是直接覆盖 |
upper_bound(key1) | 返回第一个大于等于key1的键值所在位置的迭代器 |
lower_bound(key1) | 返回第一个大于等于key1的键值所在位置的迭代器 |
- 可以下标
- 可以'='
- 双向迭代器
6.3 multimap
可重,操作和map以及multiset类似,不再介绍
这里有更详细的:multimap,multiset,以及unordered_map
multimap
multiset
unordered_map
7.最后
老了,不想打了。
上课一小时,课后四五天
晚点把函数进行总结,包括返回值之类的全部搞定