容器适配器包装了一个底层的容器。因为需要添加和删除元素,因此这个底层容器一定不能是array
三种容器适配器的构造函数类似,都有两个,以栈为例:
stack<T> s;//创建一个空栈
stack<T> s(v);//创建一个栈,用容器v中的元素来初始化栈
栈默认使用deque作为底层,当然也可以自己指定一个具有从尾端插入和删除元素的容器作为底层,比如vector
stack<T,vector<T>> s;
s.pop();//弹出栈顶元素,但是不返回其值
s.push(val);//向栈添加元素
s.top();//返回栈顶元素,但是不将元素弹出
队列的底层容器要求有push_back(val)和pop_front()能力, 队列的底层容器默认是deque, 也可以自己定义使用list作为底层容器
q.push(val);//将元素val入队
q.pop();//出队
q.front();//返回队首元素值
q.back();//返回队尾元素的值
优先队列使用了堆这种数据结构的思想,因此优先队列使用的底层容器应该能模拟堆。这就要求底层容器具有将子节点与父节点位置的快速对应的能力,比如vector,deque,list。STL的优先队列是用数组模拟堆的,因此可选的容器有vector与deque。优先队列的默认底层容器是vector
q.pop();//弹出优先级最高的元素
q.top();//返回优先级最高的元素的值
q.push(val);//将val插入到优先队列
- 与栈和队列相比,优先队列在push和pop时,内部结构会有调整
逆波兰式,也叫后缀表达式,将运算符写在操作数之后
1. 如果E本身是一个变量或者常量,则E本身逆波兰式
2. 如果E是 E1 op E2 ,z则其逆波兰式是 E1 F2 op
3. 如果E是(E1)形式,则其逆波兰式是E1的逆波兰式
举例: (a+b)*c-(d+e)/f,这是一个中缀表达式 转换成后缀表达式的步骤如下:
根据运算级,先算减号:
((a+b)*c)((d+e)/f)-
之后对每个子表达式继续转换
((a+b)c*)((d+e)f/)-
(ab+c*)(de+f/)-
ab+c*de+f/-
1. 分配两个栈,S1存储临时运算符,S2存储当前输出序列
2. 从输入的表达式左侧开始取字符,如果是数字,则入栈S2,
3. 如果取出是运算符,且优先级大于栈S1的栈顶运算符(括号不参与比较),则该运算符入栈S1,否则将S1的栈顶元素出栈到S2,
直到遇到S1的栈顶元素优先级大于当前从表达式取出的运算符,然后将取出的运算符入栈S1。
---为了保证栈S1中只有运算符且越靠近栈顶的运算符优先级越高
4. 如果取出的是左括号,直接入栈S1
5. 如果取出的是右括号,则将S1中的元素逐个出栈到S2,直到遇到一个左括号,然后舍弃这个左括号
6. 重复2~5,直到表达式取完字符,然后将栈S1中的运算符逐个出栈到S2
此时S2从栈底到栈顶的输出即为所求
- 关于运算符的优先级,如果当前取出的运算符与栈S1的栈顶运算符优先级相同,则应该按照当前取出的运算符的优先级小于S1的栈顶运算符来处理。 因为同样的优先级下,栈S1中的运算符出现的早,因此应该先处理
假设逆波兰式放置在一个数组中,另外设置一个栈S 对数组从前往后遍历,每次执行如下操作
1. 如果是数字,则入栈
2. 如果是运算符,则出栈两个元素,作为运算符的操作数,将运算结果入栈
- 文件RPN.cpp实现了中缀表达式到逆波兰式的转换与求值
波兰式也叫前缀表达式
与中缀求后缀的方法类似,只需要把表达式由从左边获取字符改为从右边开始获取字符,
运算符优先级小于S1栈顶元素时入栈S1,
将对左括号的处理改为对右括号的处理,
最后将栈S2中的元素从栈顶到栈底输出即为所求表达式
将表达式从左向右入栈,一旦栈顶两个元素都是操作数,出栈三个字符,将运算结果入栈
- 对比波兰式与逆波兰式,能够发现还是逆波兰式更好用