机试_3_数据结构(一)

本章学习基本的线性数据结构,包括向量、队列和栈。这些基础的数据结构是后续很多高级内容的基础。

一、向量

数组作为一种基本的数据类型,它是有限个类型相同的变量的线性集合,组成数组的各个变量称为数组的元素。每个元素对应一个下标,用这个下标指代该元素。同时,还可通过下标直接访问数组中的任何一个元素,并且这些访问能在常数时间内完成。然而,数组存在一个限制,即必须在创建数组时确定其大小。

向量在内部使用动态分配数组的方式来存储元素,会必要时会重新分配数组。重新分配数组就需要将原来的所有元素移动到新数组中,这个操作会比较耗时,因此向量每次重新分配数组时,会多分配一些额外的存储空间,以便适应未来可能的增长。重新分配数组时,数组的大小呈双倍增长,以便在平均意义下,保证能够在常数时间内将新元素插入到向量的末尾。

所以,与数组相比,向量其实是通过消耗更多的内存来换取存储管理效率的。

注: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

题解:

image-20230120205424651
分析:
这道题是著名约瑟夫问题的变体。当约瑟夫问题的数据规模不大时,可以考虑直接利用循环队列进行模拟进行求解。
而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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

窝在角落里学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值