【算法】栈实现表达式求值(C++)(详解)

简介: 【算法】栈实现表达式求值(C++)(详解)

【栈】实现表达式求值

思路 && 理解 && 注意

给定一串表达式,字符串类型,依次遍历从头开始遍历每一个位置的内容。

第一个数字,第一个运算符先直接往栈里面push(两个不同的栈)
接着走,遇到数push进来,接着走,遇到运算符,和前面那个已经push进栈的运算符进行优先级比较,如果当前运算符优先级大,那就接着push进来,反之,pop出栈,运算前面的式子之和(之后判断运算符栈中是否还有内容,并且当前运算符的优先级是否小于等于已有的运算符,小于等于就接着运算前面的表达式,完成push当前运算符,反之继续往下遍历push...pop...),直到最后一个元素。

注意;

一直发生变化的是rdata-右操作数,所以每次压完运算符找新的右操作数都会将他置空,准备重新赋值。

没有添加括号优先级运算。

expression.h

#pragma once
#include<iostream>
using namespace std;


#define MAX_SIZE 128

typedef struct _Postion//地图中点的坐标,这个栈中存的元素就是点的坐标
{
    int _x;
    int _y;
}Postion;

typedef int DataType;


//栈的结构体
typedef struct _Stack
{
    DataType* top;
    DataType* base;
}Stack;

//栈的初始化
bool initStack(Stack& S)
{
    S.base = new DataType[MAX_SIZE];
    if (!S.base)
    {
        return false;
    }
    S.top = S.base;
    return true;
}

//入栈
bool pushStack(Stack& S, DataType data)
{
    if (!S.base)
    {
        return false;
    }
    if (S.top - S.base == MAX_SIZE)
    {
        return false;
    }

    *(S.top) = data;
    S.top++;
    return true;
}

//出栈
bool popStack(Stack& S, DataType& e)
{
    if (S.top == S.base)
    {
        return false;
    }
    e = *(--S.top);
    return true;
}

//返回栈顶元素         
DataType* getTop(Stack& S)
{
    if (S.top - S.base == 0)
    {
        return NULL;
    }
    //注意何时自增何时不自增
    return S.top - 1;//返回栈顶元素的指针
}

//返回栈中元素个数
int getSize(Stack& S)
{
    return S.top - S.base;
}

//判断栈是否为空
bool isEmpty(Stack& S)
{
    if (S.top == S.base)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//销毁栈
void destoryStack(Stack& S)
{
    if (S.base)
    {
        delete[] S.base;
        S.top = S.base = NULL;
    }
}

experssion.cpp

#include"expression.h"
#include<iostream>
using namespace std;

//比较 lhs 的优先级是否高于 rhs,rhs 表示栈顶的符号

bool isLarger(const int &lhs, const int &rhs)
{
    if ((rhs == '+' || rhs == '-') && (lhs == '*' || lhs == '/'))
    {
        return true;
    }
    return false;
}

//计算左右操作数+运算符 (对运算符求值)
int operate(int left, int right, int op)
{
    int result = 0;
    switch (op)
    {
    case '+':
        result = left + right;
        break;
    case '-':
        result = left - right;
        break;
    case '*':
        result = left * right;
        break;
    case '/':
        result = left / right;
        break;
    default:
        break;
    }
    return result;
}

//运算主体
int calculate(string input)
{
    Stack data_stack;//操作数堆栈
    Stack opt_stack;//运算符堆栈

    int status = 0;//0接收左操作数,1接收操作符,2,接收右操作数

    //左右操作数
    //一直在发生变化的是右操作符
    int ldata = 0;
    int rdata = 0;

    char last_opt = '\0';

    //初始化堆栈
    initStack(data_stack);
    initStack(opt_stack);

    //从第一个开始遍历
    for (int i = 0; i < input.length(); i++)
    {
        if (isspace(input[i]))//跳过空白符
        {
            continue;
        }

        //不是空白,第一次到这里,默认是status = 0是左操作数
        switch (status)
        {
            //isdigit-判断是否是十进制数字
        case 0:
            //得到做操作数左操作数
            /*
                左操作数是如何得到的
                遍历字符串,第一个得到的肯定是左操作数,但我们不知道它是几位数。默认ldata为0
                其实就是——这个数是几位,这个if()条件就能进来几次
                累加在ldata中,得到左操作数
            */
            if (isdigit(input[i]))
            {
                ldata *= 10;
                ldata += input[i] - '0';//求出该位上这个数是几
            }
            
            //什么时候执行到这里?
            //第一个数字得到之后,也就是得到了ldata之后
            else
            {

                pushStack(data_stack, ldata);//左操作数进栈

                //现在input[i]的位置是运算符
                //因为结束case结束之后,出来for循环还得++,这样就错过这个运算符了
                //为了保证到case 1的语句中此时的input[i]是运算符,所以要字先--
                i--;

                status = 1;//操作数确定了,下一个就该运算符了。
            }
            break;



        case 1://遇到操作符
            if (input[i] == '+' || input[i] == '-' || input[i] == '*' || input[i] == '/')
            {
                if (isEmpty(opt_stack))//第一个运算符暂时不做任何处理,先入栈保存
                {
                    pushStack(opt_stack, input[i]);//第一个操作符进栈
                    //运算符进栈存的是对应符号的ASCII码

                    status = 2;//状态标记为2 下一个为右操作数
                }
                else//不是第一个运算符,那么就将这个与之前的做优先级比较,如果这个优先级高,那就先算这个
                {
                

                    //当前运算符高于前一个运算符

                                //当前input[i]运算符  栈里面的存的第一个运算符
                    if (isLarger(input[i], *getTop(opt_stack)))//如果当前运算符的优先级高于前一个
                    {
                        //压进栈
                        pushStack(opt_stack, input[i]);//操作符入栈
                        
                        status = 2;//下一个是右操作数
                        rdata = 0;//将右操作数置空
                    }
                    else//当前运算符的优先级小于(等于)前一个(栈顶)运算符。则计算前一个运算符的值
                    {
                        int right = 0;
                        int left = 0;
                        int opt = 0;

                        do
                        {
                            //拿到操作符 和 前面两个左右操作数
                            //先取到右边的,在取左边的(倒着拿出来)
                            //运算的时候注意参数传递顺序
                            popStack(data_stack, right);
                            popStack(data_stack, left);
                            popStack(opt_stack, opt);
                            
                            int result = operate(left, right, opt);
                            pushStack(data_stack, result);//得到一部分的结果压进栈
                        } while (!isEmpty(opt_stack) && !isLarger(input[i],*getTop(opt_stack)));//自动再往前判断,是否可以对前面的表达式进行运算
                        //运算符栈不为空 并且当前运算符优先级小于等于栈顶运算符(前面的)那么就能一并进行运算

                        //将当前input[i]运算符压入栈
                        pushStack(opt_stack, input[i]);

                        status = 2;//去右操作数
                        rdata = 0;//置空
                    }
                }
            }
            else if (input[i] == '=')//到达结尾
            {
                int opt = 0;
                int result = 0;
                do
                {
                
                    popStack(data_stack, rdata);
                    popStack(data_stack, ldata);
                    popStack(opt_stack, opt);

                    result = operate(ldata, rdata, opt);
                    pushStack(data_stack, result);
                } while (!isEmpty(opt_stack));

                //返回得到最后结果
                return result;
            }
            else
            {
                cerr << "运算符输入错误" << endl;
            }
            break;
        
        case 2://右操作数
            if (isdigit(input[i]))//同上求左操作数,求出rdata右操作数
            {
                rdata *= 10;
                rdata += input[i] - '0';
            }
            else
            {
                pushStack(data_stack, rdata);//右操作数入栈
                i--;
                status = 1;
            }
            break;
        }
    }
    return -1;
}


int main(void)
{
    string str = "12+3*6/3+4*5=";
    cout << calculate(str) << endl;//38
    return 0;
}
相关文章
|
8月前
|
存储 监控 算法
基于 C++ 哈希表算法实现局域网监控电脑屏幕的数据加速机制研究
企业网络安全与办公管理需求日益复杂的学术语境下,局域网监控电脑屏幕作为保障信息安全、规范员工操作的重要手段,已然成为网络安全领域的关键研究对象。其作用类似网络空间中的 “电子眼”,实时捕获每台电脑屏幕上的操作动态。然而,面对海量监控数据,实现高效数据存储与快速检索,已成为提升监控系统性能的核心挑战。本文聚焦于 C++ 语言中的哈希表算法,深入探究其如何成为局域网监控电脑屏幕数据处理的 “加速引擎”,并通过详尽的代码示例,展现其强大功能与应用价值。
197 2
|
10月前
|
存储 负载均衡 算法
基于 C++ 语言的迪杰斯特拉算法在局域网计算机管理中的应用剖析
在局域网计算机管理中,迪杰斯特拉算法用于优化网络路径、分配资源和定位故障节点,确保高效稳定的网络环境。该算法通过计算最短路径,提升数据传输速率与稳定性,实现负载均衡并快速排除故障。C++代码示例展示了其在网络模拟中的应用,为企业信息化建设提供有力支持。
302 15
|
10月前
|
存储 算法 数据处理
公司局域网管理中的哈希表查找优化 C++ 算法探究
在数字化办公环境中,公司局域网管理至关重要。哈希表作为一种高效的数据结构,通过哈希函数将关键值(如IP地址、账号)映射到数组索引,实现快速的插入、删除与查找操作。例如,在员工登录验证和设备信息管理中,哈希表能显著提升效率,避免传统线性查找的低效问题。本文以C++为例,展示了哈希表在局域网管理中的具体应用,包括设备MAC地址与IP分配的存储与查询,并探讨了优化哈希函数和扩容策略,确保网络管理高效准确。
|
8月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
219 17
|
6月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
192 0
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
581 77
|
7月前
|
机器学习/深度学习 存储 算法
基于 C++ 布隆过滤器算法的局域网上网行为控制:URL 访问过滤的高效实现研究
本文探讨了一种基于布隆过滤器的局域网上网行为控制方法,旨在解决传统黑白名单机制在处理海量URL数据时存储与查询效率低的问题。通过C++实现URL访问过滤功能,实验表明该方法可将内存占用降至传统方案的八分之一,查询速度提升约40%,假阳性率可控。研究为优化企业网络管理提供了新思路,并提出结合机器学习、改进哈希函数及分布式协同等未来优化方向。
232 0
|
9月前
|
存储 监控 算法
基于 C++ 哈希表算法的局域网如何监控电脑技术解析
当代数字化办公与生活环境中,局域网的广泛应用极大地提升了信息交互的效率与便捷性。然而,出于网络安全管理、资源合理分配以及合规性要求等多方面的考量,对局域网内计算机进行有效监控成为一项至关重要的任务。实现局域网内计算机监控,涉及多种数据结构与算法的运用。本文聚焦于 C++ 编程语言中的哈希表算法,深入探讨其在局域网计算机监控场景中的应用,并通过详尽的代码示例进行阐释。
201 4
|
10月前
|
存储 算法 安全
企业员工数据泄露防范策略:基于 C++ 语言的布隆过滤器算法剖析[如何防止员工泄密]
企业运营过程中,防范员工泄密是信息安全领域的核心议题。员工泄密可能致使企业核心数据、商业机密等关键资产的流失,进而给企业造成严重损失。为应对这一挑战,借助恰当的数据结构与算法成为强化信息防护的有效路径。本文专注于 C++ 语言中的布隆过滤器算法,深入探究其在防范员工泄密场景中的应用。
237 8
|
11月前
|
存储 监控 算法
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
在数字化办公时代,公司监控上网软件成为企业管理网络资源和保障信息安全的关键工具。本文深入剖析C++中的链表数据结构及其在该软件中的应用。链表通过节点存储网络访问记录,具备高效插入、删除操作及节省内存的优势,助力企业实时追踪员工上网行为,提升运营效率并降低安全风险。示例代码展示了如何用C++实现链表记录上网行为,并模拟发送至服务器。链表为公司监控上网软件提供了灵活高效的数据管理方式,但实际开发还需考虑安全性、隐私保护等多方面因素。
232 0
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨

热门文章

最新文章