算法基础课笔记(十四)

2.11:练习

2.11.1栈.acwing.3302. 表达式求值(模板题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
给定一个表达式,其中运算符仅包含 +,-,*,/(加 减 乘 整除),可能包含括号,请你求出表达式的最终值。

注意:
数据保证给定的表达式合法。
题目保证符号 - 只作为减号出现,不会作为负号出现,例如,-1+2,(2+2)*(-(1+1)+2) 之类表达式均不会出现。
题目保证表达式中所有数字均为正整数。
题目保证表达式在中间计算过程以及结果中,均不超过 2^311
题目中的整除是指向 0 取整,也就是说对于大于 0 的结果向下取整,例如 5/3=1,对于小于 0 的结果向上取整,例如 5/(14)=−1
C++和Java中的整除默认是向零取整;Python中的整除"//"默认向下取整,因此Python的eval()函数中的整除也是向下取整,在本题中不能直接使用。

输入格式
共一行,为给定表达式。

输出格式
共一行,为表达式的结果。

数据范围
表达式的长度不超过 10^5

输入样例:
(2+2)*(1+1)
输出样例:
8

中缀表达式求值问题,经典例题了。

题目中的符号都规定为二元运算符,不会作为正负号出现。

中缀表达式需要括号来处理优先级,而后缀表达式不需要括号。

将表达式画成一棵树,如何判断某棵子树被遍历完?

当前运算的优先级 <= 上一个运算的优先级。

参考题解: https://www.acwing.com/solution/content/40978/。(模拟运算流程)

“表达式求值”问题,两个核心关键点:

(1)双栈,一个操作数栈,一个运算符栈;

(2)运算符优先级,栈顶运算符,和,即将入栈的运算符的优先级比较:

如果栈顶的运算符优先级低,新运算符直接入栈;

如果栈顶的运算符优先级高,先出栈计算,新运算符再入栈。

这里通过哈希表来表示优先级表。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <unordered_map>
#include <cstring>
#include <stack>
using namespace std;
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0)

stack<int> num;
stack<char> op;

void eval(){// 进行一次二元运算求值
int b = num.top();num.pop();// 第二个操作数
int a = num.top();num.pop();// 第一个操作数
char c = op.top();op.pop();// 运算符
int x;
if (c == '*') x = a*b;
if (c == '/') x = a/b;
if (c == '+') x = a+b;
if (c == '-') x = a-b;
num.push(x);
}

int main(){
IOS;

string str;
cin >> str;
// 哈希表作为优先级表
unordered_map<char,int> pr{{'+',1},{'-',1},{'*',2},{'/',2}};

for (int i = 0; i < str.size(); i ++ ){
char c = str[i];
if (isdigit(c)){// 双指针提取出十进制的操作数
int x = 0,j = i;
// 数字转十进制
while (j < str.size() && isdigit(str[j])) x = x*10 + str[j++] - '0';
i = j - 1;// for循环中有i ++
num.push(x);// 操作数x入栈
}
else if (c == '(') op.push(c);// 左括号直接入栈
else if (c == ')'){
while (op.top() != '(') eval();// 括号优先级最高,计算括号内式子
op.pop();// 将左括号出栈
}
else{// '('不在优先级表map中,默认的值为0
// 待入栈运算符优先级低,则先计算,若优先级相同,也要从左往右先计算
// op.top() != '('可以不加
// 因为(不在map中默认值为0,一定不满足pr[op.top()] >= pr[c]
while (op.size() && op.top() != '(' && pr[op.top()] >= pr[c]) eval();
op.push(c);// +-*/操作符入栈
}
}

while (op.size()) eval();// 处理栈内剩余运算
cout << num.top() << '\n';// 操作数栈顶即为表达式最终答案
return 0;
}

2.12:C++ STL

参考资料1: https://wenku.baidu.com/view/93f33b3b192e45361066f5eb?pcf=2&qq-pf-to=pcqq.group。

参考资料2: https://oi-wiki.org/lang/csl/container/。

参考资料3: C++语法总结STL。

参考资料4:算法进阶指南。

推荐查找手册: https://www.acwing.com/blog/content/844/。

C++ STL简介:y总模板。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
vector, 变长数组,倍增的思想
size() 返回元素个数
empty() 返回是否为空
clear() 清空
front()/back() 返回第一个/最后一个数
push_back()/pop_back()
begin()/end() 返回迭代器,迭代器类似指针
[]
支持比较运算,按字典序

pair<int, int>
first, 第一个元素
second, 第二个元素
支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
make_pair(10,12);/{10,12}; 两种初始化方式

string,字符串
size()/length() 返回字符串长度
empty()
clear()
substr(起始下标,(子串长度)) 返回子串,第2个参数与java等语言不一样(java是截至位置)
c_str() 返回字符串所在字符数组的起始地址

queue, 队列
size()
empty()
push() 向队尾插入一个元素
front() 返回队头元素
back() 返回队尾元素
pop() 弹出队头元素

priority_queue, 优先队列,默认是大根堆
size()
empty()
push() 插入一个元素
top() 返回堆顶元素
pop() 弹出堆顶元素
定义成小根堆的方式:
1.priority_queue<int, vector<int>, greater<int>> q;
2.插入的所有元素x,全部取反插入-x

stack, 栈
size()
empty()
push() 向栈顶插入一个元素
top() 返回栈顶元素
pop() 弹出栈顶元素

队列和栈没有clear函数,需要重新用空参构造器创建!

deque, 双端队列,双端的vector,但性能要差
size()
empty()
clear()
front()/back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]

set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
size()
empty()
clear()
begin()/end()
迭代器++, -- 返回前驱和后继,时间复杂度 O(logn)

set/multiset 前者去重,后者不去重!
insert() 插入一个数
find() 查找一个数,不存在则返回end迭代器
count() 返回某一个数的个数
erase()
(1) 输入是一个数x,删除所有x O(k + logn)
(2) 输入一个迭代器,删除这个迭代器
lower_bound()/upper_bound() :set的核心操作!
lower_bound(x) 返回大于等于x的最小的数的迭代器
upper_bound(x) 返回大于x的最小的数的迭代器
map/multimap
insert() 插入的数是一个pair
erase() 输入的参数是pair或者迭代器
find()
[] 注意multimap不支持此操作。 时间复杂度是 O(logn)
lower_bound()/upper_bound()
// 上面是排序的,下面是无序的
unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
和上面类似,增删改查的时间复杂度是 O(1)
不支持排序相关操作 lower_bound()/upper_bound(), 迭代器的++,--

bitset, 压位,存储二进制数位
bitset<10000> s;
支持位运算:~, &, |, ^
>>, <<
==, !=
[]

count() 返回有多少个1

any() 判断是否至少有一个1
none() 判断是否全为0

set() 把所有位置成1
set(k, v) 将第k位变成v
reset() 把所有位变成0
flip() 等价于~
flip(k) 把第k位取反

系统为某一程序分配空间时,所需时间与空间大小无关,而与申请次数有关。

vector的原理就是减少申请次数,但会浪费空间。(变长数组,倍增思想)

C++ bitset——高端压位卡常题必备STL

bitset存储二进制数位。

bitset就像一个bool类型的数组一样,但是有空间优化——bitset中的一个元素一般只占1 bit,相当于一个char元素所占空间的八分之一。

坚持原创技术分享,您的支持将鼓励我继续创作!