系统学习
建议搭配 2022 年 8 月学习记录 - 队列、栈及其应用 一起使用。
我的学习资料
BiliBili:清华算协暑期培训 第二讲 STL & 基础数据结构
栈(Stack)
特征:先进后出/后进先出
1 2 3 4 5 6
| #include<stack> std::stack<T> s; s.push(x); s.pop(); s.top();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| const int N = 10; T stack[N]; int top=0;
bool empty(int top){ return top==0; }
bool full(int top){ return top==n; }
bool push(T stack[],T x){ if(full){ return 0; } stack[top++]=x; return 1; }
bool pop(T stack[],T *x){ if(empty){ return 0; } x=stack[--top]; return 1; }
bool top(T stack[],T *x){ if(sempty){ return 0; } x=stack[top]; return 1; }
|
卡特兰数(Catalan)
应用:括号序列、三角剖分、二叉树计算
队列(Queue)
特性:先进先出。
1 2 3 4 5 6 7
| #include<queue> std::queue<T> q; q.push(x); q.pop(); q.front(); q.back();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| const int N = 10; T queue[N]; int head=0,tail=0;
bool size(T queue[]){ return tail-head; }
bool empty(){ return head==tail; }
bool push(T queue[],T x){ if(){ return 0; } queue[tail++]=x; return 1; }
bool pop(T queue[],T *x){ if(empty){ return 0; } x=queue[head++]; return 1; }
bool front(int *x){ if(empty){ return 0; } x=queue[head]; return 0; }
bool back(int *x){ if(empty){ return 0; } x=queue[tail]; return 0; }
|
向量(Vector)
一句话概括:不定长的数组。
1 2 3 4 5 6 7
| #include<vertor> std::vector<T> v; v.insert(iter,x); v.push_back(); v.erase(iter); v.pop_back();
|
列表(List)与链表(linked list)
1 2 3 4 5 6 7 8 9 10 11
| #include<list> std::list<T> l; l.front(); l.back(); l.insert(iter,x); l.push_back(); l.push_front(); l.erase(iter); l.pop_back(); l.pop_front();
|
链表的结构
哨兵节点:
头元素和尾元素的处理:可以不用考虑边界情况
单向链表:
1 2 3 4 5
| struct Node { int val; Node *next; }; std::forward_list (C++11)
|
基本操作 - 插入
1 2 3 4 5 6 7
| Node* insert(Node *pos,int value){ Node *n = new Node(value); n->next = pos->next; post->next = n; return n; }
|
基本操作 - 删除
1 2 3 4 5
| void erase(Node *pos){ Node *tmp = pos->next; pos->next = pos->next->next; delete tmp; }
|
翻转 - reverse()
将一个链表整体翻转
1
| std::forward_list::reverse()
|
双向链表:
1 2 3 4
| struct Node{ int val; Node *prev,*next; };
|
基本操作 - 插入
1 2 3 4 5 6 7 8 9
| Node* insert(Node *pos,int value){ Node *n = new Node(value); n->next = pos->next; n->prev = pos; pos->next->prev = n; pos->next = n; return n; }
|
基本操作 - 删除
1 2 3 4 5 6 7
| void erase(Node *pos){ pos->prev->next = pos->next; pos->next->prev = pos->prev; delete pos; }
|
翻转 - reverse()
将一个链表整体翻转
交换 prev 和 next (首尾特别处理)
通用
基本操作 - 遍历
1
| for (Node* x = head->next; x != tail; x = x->next);
|
一些技巧
内存池
优化 new 和 delete 的时间复杂度
1 2 3 4 5
| Node pool[MAXN]; int top; Node* newnode(){ return &pool[top++]; }
|
数组下标模拟指针
1 2 3 4 5 6 7
| struct Node{ int val,next; } pool[MAXN]; int top; int newnode(){ return top++; }
|
优先队列(Priority Queue)
特征:“优先级”大的先离开
1 2 3 4 5 6 7
| #include<queue> std::priority_queue<T> pq; pq.push(x); pq.pop(); pq.top();
std:greater<T>
|
实现:二叉树
其他 STL
容器类
-
set/map
-
deque
算法类
-
sort:排序
-
lower_bound:二分查找(大于等于)
-
upper_bound:二分查找(大于)
-
merge:归并
一些应用
单调栈 单调队列
-
例题给出 n 天的气温情况,对于每一天,求出前k天的气温最大值。
-
关键的观察:如果存在两个数满足 $ i<j∧a_i<a_j $ ,那么在a被纳入考察之后,a 就不可能成为最大值了。(如果有人比你年轻还比你强)
-
那么我们需要考虑的可能值是单调递减的。
-
从左到右扫描,每加入一个数,可以排除最末尾的一段数的可能性。
-
同时我们只需要维护前k天的情况,因此过期的数据也可排除。
-
我们需要实现 push_back,pop_back,pop_front。
表达式求值
-
为了简化问题,假设我们只有 $ + $ $ - $ $ * $ $ / $ $ () $ 这几种运算。
-
观察:1+2∗3 和 1∗2+3 。我们什么时候可以确定计算顺序?
-
第一个等式需要到最末尾,第二个等式在扫描到 + 的时候就发现可以计算前面的内容。
-
无法判断的式子优先级大小递增。
-
类似单调队列的单调栈。同时需要维护没有计算的数。
-
将括号也纳入优先级比较的范围,或者每遇到一层括号,括号内优先级增加一档。