本章学习基本的线性数据结构,包括向量、队列和栈。这些基础的数据结构是后续很多高级内容的基础。
一、向量
数组作为一种基本的数据类型,它是有限个类型相同的变量的线性集合,组成数组的各个变量称为数组的元素。每个元素对应一个下标,用这个下标指代该元素。同时,还可通过下标直接访问数组中的任何一个元素,并且这些访问能在常数时间内完成。然而,数组存在一个限制,即必须在创建数组时确定其大小。
向量在内部使用动态分配数组的方式来存储元素,会必要时会重新分配数组。重新分配数组就需要将原来的所有元素移动到新数组中,这个操作会比较耗时,因此向量每次重新分配数组时,会多分配一些额外的存储空间,以便适应未来可能的增长。重新分配数组时,数组的大小呈双倍增长,以便在平均意义下,保证能够在常数时间内将新元素插入到向量的末尾。
所以,与数组相比,向量其实是通过消耗更多的内存来换取存储管理效率的。
注:C++内部对向量(vector)进行了封装,在会使用的前提下,有兴趣可以研究一下内部的实现细节。
1、STL-vector
1.1、vector的定义
添加头文件 #include <vector>
定义一个向量 vector<typename> name, 其中typename是向量元素的数据类型,name是向量的名字
1.2、vector的状态
vector中用做判断的状态通常有两个:
1. empty( ): 返回当前向量是否为空
2. size( ): 返回当前向量元素个数
1.3、vector尾部元素的添加和删除
push_back(): 向尾部添加新的元素
pop_back(): 删除尾部的元素
1.4、vector元素的访问
两种访问方式:
1. 数组一般通过元素下标进行访问,下标的区间为[0, size()-1]。
2. 迭代器进行访问,迭代器类似于指针。
1.5、vector元素操作
vector中的常用元素操作包括:
insert(): 在任意位置插入元素
erase(): 在任意位置删除元素
clear(): 将向量清空
1.6、vector迭代器操作
vector中常用的迭代器操作包括:
begin(): 返回向量中的首元素的迭代器
end(): 返回向量中尾元素后一个位置的迭代器
1.7、示例代码
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
//定义一个向量
vector<int> myVector;
for (int i = 0; i < 5; ++i) {
//在尾部逐一插入元素
myVector.push_back(i);
}
cout << "向量的长度为:" << myVector.size() << endl;
//在头部插入3个666
myVector.insert(myVector.begin(), 3, 666);
//输出向量--数组输出
for (int j = 0; j < myVector.size(); ++j) {
cout << myVector[j] << " ";
}
cout << endl;
cout << "向量的长度为:" << myVector.size() << endl;
//弹出尾部元素
myVector.pop_back();
cout << "向量的长度为:" << myVector.size() << endl;
cout << "第5个元素为:" << myVector[4] << endl;
//删除索引为5及其后续的元素
myVector.erase(myVector.begin() + 5, myVector.end());
//输出向量--迭代器输出
vector<int>::iterator it;
for (it = myVector.begin(); it != myVector.end(); it++) {
cout << *it << " ";
}
cout << endl;
//清空向量
myVector.clear();
cout << "向量的长度为:" << myVector.size() << endl;
return 0;
}
向量的长度为:5
666 666 666 0 1 2 3 4
向量的长度为:8
向量的长度为:7
第5个元素为:1
666 666 666 0 1
向量的长度为:0
2、完数与盈数–清华大学
描述:
一个数如果恰好等于它的各因子(该数本身除外)子和,如:6=3+2+1,则称其为“完数”;若因子之和大于该数,则称其为“盈数”。求出2 到60 之间所有“完数”和“盈数”,并以如下形式输出: E: e1 e2 e3 …(ei 为完数) G: g1 g2 g3 …(gi 为盈数)
输入描述:
无输入数据
输出描述:
按描述要求输出(注意EG后面的冒号之后有一个空格)。
示例1
输入:
无
输出:
无
题解:
分析:
只要num % i == 0, 则i就是num的一个因子
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
/**
* 求一个整数的各因子之和
* @param num
* @return
*/
int getFactorSum(int num) {
int sum = 0;
for (int i = 1; i < num; ++i) {
if (num % i == 0) {
sum += i;
}
}
return sum;
}
/**
* 完数与盈数--清华大学
* @return
*/
int main() {
//数目未知,用vector存放
vector<int> numberE;
vector<int> numberG;
for (int i = 2; i <= 60; ++i) {
int factorSum = getFactorSum(i);
if (i == factorSum) {
//完数
numberE.push_back(i);
} else if (i < factorSum) {
//盈数
numberG.push_back(i);
}
}
//打印完数
cout << "E:";
for (int j = 0; j < numberE.size(); ++j) {
cout << " " << numberE[j];
}
cout << " ";
/*
* 打印盈数
* 牛客的oj有问题,很明显2不是盈数,但是牛客oj里面把2判为盈数了
* 因此,此处手动加上!!!
* 但是,再次强调,2不是盈数,只是为了通过oj
*/
numberG.insert(numberG.begin(), 2);
cout << "G:";
for (int k = 0; k < numberG.size(); ++k) {
cout << " " << numberG[k];
}
cout << endl;
return 0;
}
二、队列
队列(Queue)是一种线性的序列结构,其存放的元素按照线性的逻辑次序排列,但队列的操作只限于逻辑上的两端,即新元素只能从队列的一端插入,并且只能从另一端删除已有的元素。
允许队列插入的一端称为队列的尾部,允许队列删除的一端称为队列的头部。因此,对于元素来说,插入与删除就分别称为入队和出队。
队列中各个元素的操作次序必定遵守所谓的先进先出(First–In First-Out,FIFO)规则,即越早入队的元素将会越早出队,而越晚入队的元素将会越晚出队。
注:C++内部对队列(queue)进行了封装,在会使用的前提下,有兴趣可以研究一下内部的实现细节。
1、STL-queue
1.1、queue的定义
添加头文件 #include <queue>
定义一个队列 queue<typename> name, 其中typename是队列元素的数据类型,name是队列的名字
1.2、queue的状态
queue中用做判断的状态通常有两个:
1. empty( ): 返回当前队列是否为空
2. size( ): 返回当前队列元素个数
1.3、queue元素的添加或删除
push(): 在队尾添加新的元素
pop(): 删除队头已有的元素
1.4、queue元素的访问
队列只能对两端进行操作:
front(): 获取队友元素
back(): 获取队尾元素
1.5、示例代码
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
queue<int> myQueue;
/**
* 队列
* @return
*/
int main() {
cout << "队列的长度为:" << myQueue.size() << endl;
//向队尾添加元素
for (int i = 0; i < 10; ++i) {
myQueue.push(i);
}
cout << "队列的长度为:" << myQueue.size() << endl;
cout << "队首元素:" << myQueue.front() << endl;
cout << "队尾元素:" << myQueue.back() << endl;
//输出队列
cout << "队列元素为[对头<--队尾]: ";
while (!myQueue.empty()) {
cout << myQueue.front() << " ";
myQueue.pop();
}
cout << endl;
cout << "队列的长度为:" << myQueue.size() << endl;
return 0;
}
队列的长度为:0
队列的长度为:10
队首元素:0
队尾元素:9
队列元素为[对头<--队尾]: 0 1 2 3 4 5 6 7 8 9
队列的长度为:0
2、猫狗收容所
描述:
有家动物收容所只收留猫和狗,但有特殊的收养规则。收养人有两种收养方式:
- 第一种为直接收养所有动物中最早进入收容所的。
- 第二种为选择收养的动物类型(猫或狗),并收养该种动物中最早进入收容所的。
给定一个操作序列代表所有事件。
- 若第一个元素为1,则代表有动物进入收容所。第二个元素为动物的编号,正数代表狗,负数代表猫。
- 若第一个元素为2,则代表有人收养动物。第二个元素若为0,则采取第一种收养方式;若为1,则指定收养狗:若为-1,则指定收养猫。
请按顺序输出收养动物的序列。
若出现不合法的操作,即没有可以符合领养要求的动物,则将这次领养操作忽略。
输入:
第一个是n,它代表操作序列的次数。接下来是n行,每行有两个值m和t,分别代表题目中操作
的两个元素。
输出:
按顺序输出收养动物的序列,编号之间以空格间隔。
示例1:
输入:
6
1 1
1 -1
2 0
1 2
2 -1
2 1
输出:
1 -1 2
题解:
分析:
题目一直强调说收养“最早进入”收养所,这很自然就能联想到利用队列“先进先出”的特性来解题。
所以,我们可以维护一个队列来存放所有动物,这样一来就可以很方便地实现第一种收养方式。但是,对于第二种收养方式就比较麻烦,需要遍历整个队列才知道“最早进入”收养所的猫或者狗。
因此,我们可以维护两个队列,一个存放猫,另一个存放狗,同时对每只动物进入收养所的次序进行标记,这样一来就可以清晰地知道第一只进入收养所的动物。这种做法都可以比较方便地实现第一种收养方式和第二种收养方式。
代码稍微有点长,但是逻辑还是比较清晰的,仔细分析即可。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
struct Animal {
/**
* 动物编号
* 负数表示猫;正数表示狗
*/
int code;
/**
* 次序标志,标记进入收容所的序号
*/
int order;
/**
* 构造函数
* @param code
* @param order
*/
Animal(int code, int order) {
this->code = code;
this->order = order;
}
};
/**
* 猫狗收容所
* @return
*/
int main() {
//存放猫的队列
queue<Animal> cats;
//存放狗的队列
queue<Animal> dogs;
int n;
cin >> n;
/*
* 事件
* 1--动物进入收养所-->第二个元素为"动物编号"
* 2--收养动物-->第二个元素为"收养方式"
*/
int event;
//收养方式
int method;
//动物编号
int code;
//动物次序
int order = 0;
//临时存放第二个元素
int tmp;
for (int i = 0; i < n; ++i) {
cin >> event >> tmp;
if (event == 1) {
/*
* 动物进入收养所
* 第二个元素为"动物编号"
*/
code = tmp;
if (code > 0) {
//动物为狗
dogs.push(Animal(code, order++));
} else {
//动物为猫
cats.push(Animal(code, order++));
}
} else if (event == 2) {
/*
* 有人收养动物
* 第二个元素为"收养方式"
*/
method = tmp;
if (method == 0 && !dogs.empty() && !cats.empty()) {
//第一种收养方式--收养所猫和狗都有--最先进入收养所的动物
if (dogs.front().order < cats.front().order) {
//最先进入是狗
cout << dogs.front().code << " ";
dogs.pop();
} else {
//最先进入是猫
cout << cats.front().code << " ";
cats.pop();
}
} else if (method == 0 && !dogs.empty() && cats.empty()) {
//第一种收养方式--收养所只有狗
cout << dogs.front().code << " ";
dogs.pop();
} else if (method == 0 && dogs.empty() && !cats.empty()) {
//第一种收养方式--收养所只有猫
cout << cats.front().code << " ";
cats.pop();
} else if (method == 1 && !dogs.empty()) {
//指定收养狗
cout << dogs.front().code << " ";
dogs.pop();
} else if (method == -1 && !cats.empty()) {
//指定收养猫
cout << cats.front().code << " ";
cats.pop();
}
}
}
cout << endl;
return 0;
}
3、约瑟夫问题 No.2
描述:
个小孩围坐成一圈,并按顺时针编号为1,2,…,n,从编号为p的小孩顺时针依次报数,由1报到m,报到m时,这名小孩从圈中出去;然后下一名小孩再从1报数,报到m时再出去。
以此类推,直到所有小孩都从圈中出去。
请按出去的先后顺序输出小孩的编号。
输入:
第一个是n,第二个是p,第三个是m (0<m, n<300)。
最后一行是:000。
输出:
按出圈的顺序输出编号,编号之间以逗号间隔。
示例1
输入:
8 3 4
0 0 0
输出:
6,2,7,4,3,5,1,8
题解:
分析:
这道题是著名约瑟夫问题的变体。当约瑟夫问题的数据规模不大时,可以考虑直接利用循环队列进行模拟进行求解。
而queue只是普通的队列,所以我们把queue的队首元素弹出队列后,再次将其压入队列尾部,用queue来模拟循环队列的效果。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
/**
* 约瑟夫问题 No.2
* @return
*/
int main() {
int n;
int p;
int m;
while (cin >> n >> p >> m) {
if (n == 0 && p == 0 && m == 0) {
break;
}
queue<int> queue;
for (int i = 1; i <= n; ++i) {
//依次加入队列
queue.push(i);
}
//定位到p, 将前面的p-1个出队后,再插入队尾
for (int i = 1; i < p; ++i) {
queue.push(queue.front());
queue.pop();
}
//开始计数
while (!queue.empty()) {
//前面的m-1个出队后,再插入队尾
for (int i = 1; i < m; ++i) {
queue.push(queue.front());
queue.pop();
}
if (queue.size() == 1) {
//最后一个元素要换行
cout << queue.front() << endl;
} else {
//非最后一个元素要输出空格
cout << queue.front() << " ";
}
queue.pop();
}
}
return 0;
}
8 3 4
6 2 7 4 3 5 1 8
8 1 3
3 6 1 5 2 8 4 7
8 2 1
2 3 4 5 6 7 8 1
0 0 0
三、栈
栈(Stack)是一种线性序列结构,其存放的元素也是按照线性逻辑次序排列的。然而,栈的操作仅限于逻辑上特定的一端,即新元素只能从栈的一端插入,也只能从这一端删除已有的元素。
注:禁止操作的一端称为盲端
栈中允许元素插入和删除的一端称为栈顶,禁止操作的盲端就称为栈底。于是,插入元素和删除元素就分别称为入栈和出栈。因此,栈中各个元素的操作次序必定遵守所谓的后进先出(Last-In First–Out, LIFO)规则。
注:C++内部对栈(stack)进行了封装,在会使用的前提下,有兴趣可以研究一下内部的实现细节。
1、STL-stack
1.1、stack的定义
添加头文件 #include <stack>
定义一个栈 stack<typename> name, 其中typename是栈元素的数据类型,name是栈的名字
1.2、stack的状态
stack中用做判断的状态通常有两个:
1. empty( ): 返回当前栈是否为空
2. size( ): 返回当前栈元素个数
1.3、stack元素的添加或删除
push(): 将元素压入栈顶
pop(): 弹出栈顶元素
1.4、stack元素的访问
只能对栈的栈顶一端进行操作,所以只能用top()来访问栈顶元素
注意:队列可以访问队头和队尾,而栈不行。务必注意stack和queue的区别。
1.5、示例代码
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <stack>
using namespace std;
/**
* 栈
* @return
*/
int main() {
stack<int> stack;
cout << "栈的高度:" << stack.size() << endl;
for (int i = 0; i < 10; ++i) {
stack.push(i);
}
cout << "栈顶元素:" << stack.top() << endl;
cout << "栈的高度:" << stack.size() << endl;
int sum = 0;
while (!stack.empty()) {
sum += stack.top();
stack.pop();
}
cout << "sum: " << sum << endl;
cout << "栈是否为空:";
if (stack.empty()) {
cout << "是" << endl;
} else {
cout << "否" << endl;
}
return 0;
}
栈的高度:0
栈顶元素:9
栈的高度:10
sum: 45
栈是否为空:是
1.6、栈的应用
栈的应用:逆序输出、括号匹配问题、表达式求值
2、Zero-complexity Transposition–上海交通大学
描述:
You are given a sequence of integer numbers. Zero-complexity transposition of the sequence is the reverse of this sequence. Your task is to write a program that prints zero-complexity transposition of the given sequence.
题目大意:给定一个整数序列,序列的零复杂性换位是将序列反转。写一个程序,对这个序列进行零-复杂性换位。
输入:
For each case, the first line of the input file contains one integer n-length of the sequence (0 < n ≤ 10 000). The second line contains n integers numbers-a1, a2, …, an (-1 000 000 000 000 000 ≤ ai ≤ 1 000 000 000 000 000).
输出:
For each case, on the first line of the output file print the sequence in the reverse order.
示例1
输入:
5
-3 4 6 -8 9
输出:
9 -8 6 4 -3
题解:
逆序输出,即先进后出,所以可以很自然联想到"栈".
不过要注意a的取值范围:-1000000000000000 <= a <= 1000000000000000
所以,此题要要用long long类型去接收,因为int会出现溢出。
int最大为214748364
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <stack>
using namespace std;
/**
* Zero-complexity Transposition--上海交通大学
* @return
*/
int main() {
int n;
while (cin >> n) {
long long num;
stack<long long> stack;
for (int i = 0; i < n; ++i) {
cin >> num;
stack.push(num);
}
//循环取栈顶元素,即可完成逆序输出
while (!stack.empty()) {
if (stack.size() == 1) {
//最后一个数字换行
cout << stack.top() << endl;
} else {
//非最后一个输出空格
cout << stack.top() << " ";
}
stack.pop();
}
}
return 0;
}
3、括号匹配问题
描述:
在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来的字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用”$”标注,不能匹配的右括号用”?"标注。
输入:
输入包括多组数据,每组数据一行,包含一个字符串,只包含左右括号和大小写字母,字符串长度不超计100
输出:
对每组输出数据,输出两行,第一行包含原始输入字符,第二行由” ””?”和空格组成,” ””?”和空格组成,” ””?”和空格组成,””和”?表示与之对应的左括号和右括号不能匹配。
示例1
输入:
)(rttyy())sss)(
输出:
)(rttyy())sss)(
? ?$
题解:
分析:
结题的关键是:每个右括号必定要与之前未匹的左括号中最靠右的一个进行匹配。
明白这个匹配规则之后,我们只需从左往右
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <stack>
using namespace std;
/**
* 括号匹配问题
* @return
*/
int main() {
stack<int> stack;
string str;
while (cin >> str) {
int length = str.size();
//标记字符串
string flagStr(length, ' ');
/*
* 从左往右扫描
* 1 左括号,直接入栈(注:索引入栈)
* 2 右括号,弹出栈顶进行匹配
* 注:因为括号就两种,因此只需检查栈是否为空即可
* 2.1 若不为空,则匹配成功
* 2.2 若为空,则匹配失败,标记该右括号(?)
*/
for (int i = 0; i < length; ++i) {
if (str[i] == '(') {
//左括号
stack.push(i);
} else if (str[i] == ')') {
//右括号
if (!stack.empty()) {
//匹配成功
stack.pop();
} else {
//标记该索引的右括号
flagStr[i] = '?';
}
}
}
/*
* 扫描结束后,检查栈是否为空
* 1 栈为空,则括号匹配成功
* 2 栈不为空,则标记该左括号($)
*/
while (!stack.empty()) {
int index = stack.top();
flagStr[index] = '$';
stack.pop();
}
//输出原字符串
cout << str << endl;
//输出标记字符串
cout << flagStr << endl;
}
return 0;
}
)(rttyy())sss)(
)(rttyy())sss)(
? ?$
(wew)(dsa)(((ds)()dsd)
(wew)(dsa)(((ds)()dsd)
$
(s)sss(sss)))))(((()))
()sss(sss)))))(((()))
????$
4、简单计算器–浙江大学
描述:
读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
输入描述:
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。
输出描述:
对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。
示例1
输入:
1 + 2
4 + 2 * 5 - 7 / 11
0
输出:
3.00
13.36
题解:
此类型的题应该算是比较难的一类题了,如果没有遇到过,自己很难想到方法,但是好在解题套路相对固定。
分析:
概括:中缀表达式求值 = 中缀表达式转后缀表达式 + 后缀表达式求值
我们可以先将中缀表达式转化后缀表达式,再由后缀表达式来求值。
步骤一:中缀表达式转后缀表达式
1. 初始化一个栈,用于暂存不能确定运算顺序的操作符。
2. 从左到右开始扫描,直至末尾,由3种结果。
2.1 操作数:直接加入后缀表达式。
2.2 界限符:"(",则直接入栈。
")",弹出栈内的运算符,并加入到后缀表达式中,直至弹出"("。
注:")"不加入到后缀表达式中。
2.3 运算符:依次弹出所有优先级高于或者等于当前运算符的运算符,并将其加入到后缀表达式中。
注:若碰到"("或者"栈空"则停止,再将当前运算符入栈。
3. 处理完所有字符,将栈中剩余运算符依次弹出,并加入到后缀表达式中。
步骤二:后缀表达式求值
1. 初始化一个栈,用于暂存操作数。
2. 从左向右扫描元素。
2.1 扫描到操作数,则入栈。
注:操作数可能是不止一位,因此要进行判断。
2.2 扫描到操作符,则执行2。
3. 弹出两个栈顶元素,执行相应运算,再将其结果压入栈中。
注:先出栈的是"右操作数",注意操作数的顺序。
4. 继续执行2,直至元素全部处理完为止。
5. 栈中有且仅有的唯一元素即为结果。
下述代码分别将"中缀表达式转后缀表达式"、"后缀表达式求值"封装成两个函数。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <stack>
#include <map>
using namespace std;
/**
* 中缀表达式转后缀表达式(逆波兰式)
* @param formula
* @return
*/
string getRPN(string formula);
/**
* 后缀表达式求值
* @param rpnFormula
* @return
*/
double getRPNRes(string rpnFormula);
/**
* 计算运算符的操作结果
* @param opera 运算符
* @param leftNum 左操作数
* @param rightNum 右操作数
* @return
*/
double calculate(char opera, double leftNum, double rightNum);
/**
* map存放运算符的优先级
*/
map<char, int> priority = {
{'$', 0}, //结尾符$--1
{'+', 1}, //加--1
{'-', 1}, //减--1
{'*', 2}, //乘--2
{'/', 2} //除--2
};
/**
* 简单计算器--浙江大学
* @return
*/
int main() {
string str;
while (getline(cin, str)) {
if (str == "0") {
break;
}
//在字符串的结尾添加"$"
str += "$";
string formula = getRPN(str);
double res = getRPNRes(formula);
printf("%.2f\n",res);
//cout << formula << endl;
//cout << res << endl;
}
return 0;
}
string getRPN(string formula) {
stack<char> operaStack;
string res = "";
string num = "";
for (int i = 0; i < formula.size(); ++i) {
char character = formula[i];
if (character == '$') {
//来到最后一个字符$
//将num加入道后缀表达式中
res += num;
res += " ";
//将字符栈中剩余的运算符加入道后缀表达式中
while (!operaStack.empty()) {
res += operaStack.top();
operaStack.pop();
res += " ";
}
} else if (character == ' ') {
//空格, 则表示一个单位已经扫描完
if (num != "") {
res += num;
res += " ";
num = "";
}
} else if ('0' <= character && character <= '9' || character == '.') {
//数字
num.push_back(character);
} else if (('a' <= character && character <= 'z') || ('A' <= character && character <= 'Z')) {
res += character;
res += " ";
} else if (character == '(') {
//"(", 则直接入栈
operaStack.push(character);
} else if (character == ')') {
//")", 则弹出栈内的运算符,并加入到后缀表达式中,直至弹出"("
while (!operaStack.empty() && operaStack.top() != '(') {
res += operaStack.top();
operaStack.pop();
res += " ";
}
} else {
/*
* 运算符
* 依次弹出所有优先级高于或者等于当前运算符的运算符,并将其加入到后缀表达式中。
*/
while (!operaStack.empty() && priority[operaStack.top()] >= priority[character]) {
res += operaStack.top();
operaStack.pop();
res += " ";
}
//当前运算符入栈
operaStack.push(character);
}
}
return res;
}
double getRPNRes(string formula) {
stack<double> numStack;
string num = "";
for (int i = 0; i < formula.size(); ++i) {
char character = formula[i];
if (character == ' ') {
if (num != "") {
numStack.push(stod(num));
num = "";
}
} else if ('0' <= character && character <= '9' || character == '.') {
num.push_back(character);
} else {
double rightNum = numStack.top();
numStack.pop();
double leftNum = numStack.top();
numStack.pop();
numStack.push(calculate(character, leftNum, rightNum));
}
}
if (numStack.size() == 1) {
return numStack.top();
} else {
return -1;
}
}
double calculate(char opera, double leftNum, double rightNum) {
double res = 0;
switch (opera) {
case '+':
res = leftNum + rightNum;
break;
case '-':
res = leftNum - rightNum;
break;
case '*':
res = leftNum * rightNum;
break;
case '/':
res = leftNum / rightNum;
break;
default:
break;
}
return res;
}