今天我抽时间看了看栈的题目,写了个自以为很直观的C++代码,实话,按照我们抽象书上的编程思路,整个过程就像是有人在前面引路一样,而且我发现我的代码也规范了很多。
好,这次我先来整理笔记。上次太晚没来得及写分析,这次补上:
首先括号匹配问题是我们数据结构书上的题目,留给我们作为习题的p49,比如输入({[][]}),这样就是正确的,但是[(]就是错误的。书上的方法解释的有点啰嗦,我就按照自己的想法说了:
利用栈的原理,我们可以这样,我们知道左边的括号一定与右边的相匹配,所以我们建立一个char类型的栈,用于存放六中括号类型。其次如果放入的是左括号,那么就压入栈堆中,一旦放入的是右括号,那么就与栈顶的左括号作为比较,一旦匹配,就将左括号去除,继续下一轮的比较,如果不匹配,那么直接返回“NO”.
从下面的代码我们可以看到下面的一些函数:
- bool isLeftBrackets(char ch); 用来判断压入栈的是不是左括号
- bool isBracket(char ch); 用来判断用户输入是否为括号类型(在用户的角度思考用户可能的输入)
- bool isMatchingBracket(char lb, char rb); 用来判断两个括号是不是匹配,lb代表左括号,rb代表右括号。
- int removeBrackets(stack< char > & operandStack, char ch); 执行移除匹配括号的操作
/*括号匹配*/
#include <iostream>
#include <string>
#include <stack>
using namespace std;
/*funtion prototypes */
bool isLeftBrackets(char ch);
bool isBracket(char ch);
bool isMatchingBracket(char lb, char rb);
int removeBrackets(stack<char> & operandStack, char ch);
/*main funtion */
int main() {
while(true) {
cout << "please input brackets" << endl;
stack<char> operandStack;
string brackets;
getline(cin,brackets);
if (brackets.length() == 0) { cout << "error,please input brackets" << endl; }
for (int i = 0; i < brackets.length(); i++) {
char ch = brackets[i];
if (!isBracket(ch)) {
cout << "input error" << endl;
break;
} else if (isLeftBrackets(ch)) {
operandStack.push(ch);
} else {
removeBrackets(operandStack,ch);
}
}
if (operandStack.empty()) {
cout << "Yes" << endl;
} else {
cout << "NO" << endl;
}
}
return 0;
}
/* 检查输入的是否为括号 */
bool isBracket(char ch) {
if (isLeftBrackets(ch)) return true;
switch (ch) {
case ')': case ']': case '}':
return true;
default:
return false;
}
}
/*判断是否为左括号 */
bool isLeftBrackets(char ch) {
switch (ch) {
case '(': case '{': case '[':
return true;
default:
return false;
}
}
/*从栈中移除括号 */
int removeBrackets(stack<char> & operandStack, char ch) {
if(operandStack.empty()){
return 0;
} else {
if(isMatchingBracket(operandStack.top(), ch)) {
operandStack.pop();
} else {
return 0;
}
}
}
bool isMatchingBracket(char lb, char rb) {
if ((lb == '(' && rb == ')' ) || ( lb == '[' && rb == ']') || (lb == '{'
&& rb == '}')) {
return true;
} else {
return false;
}
}
实例如下:
口袋计算器
这个问题是抽象编程书上面的一个经典问题,堆栈的一个有趣的应用是在电子计算器中用以存储计算的中间结果,这个作用在早期科学中最容易看到。在早期被称为reverse Polish notation。简称为RPN.
在RPN当中,操作符常常在操作数的输入之后,比如下面的表达式:
8.5 * 4.4 + 6.9 / 1.5
在RPN中你是这样输入的:
enter代表输入。当你按下ENTER键时,计算机就会将先前的数压入栈中。当你输入的是运算符的时候,计算机就会检查你刚刚输入的是不是数字,如果是,那么就将它取出来,然后进行计算。因此我们可以看出来,它计算的过程就想这样
- 从栈顶中取出两个元素(Popping the top two values from the stack)
- 将你输入的运算符应用于两个数值中(Applying the arithmetic operation indicated by the button to these values)
- 把结果压回栈中(Pushing the result back on the stack)
事实上,每次用户输入一个数据,都会放在栈顶,也就是说我们看到的元素总是栈顶的元素。下面的图片说明了RPN的运行范式:
下面的代码就展示了这样一个计算器,当然我们需要思考它具备什么样的功能:
- A floating-point number(我们的操作数为浮点数)
- An arithmetic operator chosen from the set +, -, *, and / (我们可以从这四个操作符中选取操作)
- The letter Q, which causes the program to quit(按下q或Q,退出)
- The letter H, which prints a help message(按下h或者H,获得帮助菜单)
- The letter C, which clears any values left on the stack (按下c或者C,清空栈)
我写的代码如下:
/*口袋计算器 */
#include <iostream>
#include <string>
#include <cctype>
#include <stack>
#include <cstdlib>
using namespace std;
/*函数原型*/
void applyOperator(char op,stack<double> & operandStack);
void helpCommand();
void error(string msg);
void clearOperandStack(stack<double> & operandStack);
/*主函数*/
int main () {
cout << "RPN Calculator Simulation (type H for help)" << endl;
stack<double> operandStack;
while(true) {
cout << ">";
string line;
getline(cin,line);
if (line.length() == 0) {
line = "Q";
}
char ch = toupper(line[0]);
if (ch == 'Q') {
break;
} else if (ch == 'H') {
helpCommand();
} else if (ch == 'C') {
clearOperandStack(operandStack);
} else if (isdigit(ch)) {
operandStack.push(atof(line.c_str()));
} else {
applyOperator(ch,operandStack);
}
}
return 0;
}
void error(string msg) {
cerr << msg << endl;
exit(EXIT_FAILURE);
}
void applyOperator(char op, stack<double> & operandStack) {
double result;
double rhs = operandStack.top();
operandStack.pop();
double lhs = operandStack.top();
operandStack.pop();
switch (op) {
case '+': result = lhs + rhs; break;
case '-': result = lhs - rhs; break;
case '*': result = lhs * rhs; break;
case '/': result = lhs / rhs; break;
default: error("Illegal operator");
}
cout << result << endl;
operandStack.push(result);
}
void helpCommand() {
cout << "Enter expressions in Reverse Polish Notation," << endl;
cout << "in which operators follow the operands to which" << endl;
cout << "they apply. Each line consists of a number, an" << endl;
cout << "operator, or one of the following commands:" << endl;
cout << " Q -- Quit the program" << endl;
cout << " H -- Display this help message" << endl;
cout << " C -- Clear the calculator stack" << endl;
}
void clearOperandStack(stack<double> & operandStack) {
while (!operandStack.empty()) {
operandStack.pop();
}
}
书上的代码很多是他自己的接口,所以,我就改为了我们C++通用的。比如这一个atof(line.c_str() 就是先把字符串妆变为c类型的字符,然后转换为数字类型,字符串的加是连接,不是所谓的加法,所以要进行转换。这里用到了我上次说的一些我们自定义的代码,参考我上一篇的博文,C++抽象编程——STL(2)——stack类
话不多说,运行一下:
PS:在写这个程序的时候,我犯了一个很低级的错误,那就是if (ch == ‘Q’) 我硬是写成了if (ch = ‘Q’),检查还没发现,跑去论坛发帖,实在丢人。有时候一些低级错误,犯了都很难找到。真的很谢谢那位指出我错误的同学,谢谢