C++学习-数据结构-STL与基本数据结构

STL与基本数据结构

第一日集训,笔记。

0. iterator迭代器

0.1 迭代器与指针

迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,它和数组类似,但并不相同,所以先理清楚两者之间的关系:
迭代器:

  1. 迭代器它模拟了指针的一些功能,通过重载 ‘—>’、'*'、 ‘++’、‘- -’(反向迭代器) 这些操作符(还有其他的一些操作符),封装了指针,提供了比指针更强大的功能,可以看作是智能指针。
  2. 迭代器与指针还有一个较大区别就是迭代器返回的是对象的引用,而不是对象的值,所以cout只能输出迭代器使用 * 取对象值后的值,而不能直接输出其自身。

指针:

  1. 指针可以指向函数,迭代器就不可以,迭代器只能指向容器。
  2. 指针是迭代器的一种,只能用于某些特定的容器,比如数组、结构体。
  3. 迭代器是指针的抽象和泛化,但是二者还是有很大差别的。

资料来源于 https://www.cnblogs.com/buanxu/p/12756125.html

0.2 迭代器的定义

迭代器的定义方法有以下四种:
正向迭代器: 容器类名::iterator 迭代器名;
常量迭代器: 容器类名::const_iterator 迭代器名;
反向迭代器: 容器类名::reverse_iterator 迭代器名;
常量方向迭代器: 容器类名::const_reverse_iterator 迭代器名;
技巧:自动类型: auto 迭代器名;(正向)

0.3 迭代器的用法示例

  1. 无法通过常量迭代器改变迭代器指向的元素。
  2. 迭代器都可以进行++操作,反向迭代器和正向迭代器的区别在于:
    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 迭代器功能分类

常用的迭代器按功能强弱分为输入、输出、正向、双向、随机访问五种,这里只介绍常用的三种。

  1. 正向迭代器。支持以下操作:++p,p++,*p。此外,两个正向迭代器可以互相赋值,还可以用==!=运算符进行比较。
  2. 双向迭代器。双向迭代器具有正向迭代器的全部功能。除此之外,--pp--都是有定义的。--p使得 p 朝和++p相反的方向移动。
  3. 随机访问迭代器。随机访问迭代器具有双向迭代器的全部功能。若 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 , 他和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队。
优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。

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.最后

老了,不想打了。
上课一小时,课后四五天
晚点把函数进行总结,包括返回值之类的全部搞定

posted @ 2020-12-03 13:25  七铭的魔法师  阅读(123)  评论(0编辑  收藏  举报