栈应用——逆波兰算法

简介: 栈应用——逆波兰算法

前言


在我们进行数字运算时我们会根据优先级自然的将结果算出来如同喝水一样,这是因为我们在学习算数开始就已经对这种运算方式习以为常了,但只能分辨出0和1的计算机又是如何进行运算的呢?🤔🤔🤔


中缀表达式?


例如a+b,运算符在两个操作数的中间。这是我们一直习惯使用的表达式形式


后缀表达式


例如a b + ,运算符在两个操作数的后面。这是一种利于计算机处理的表达形式


逆波兰表达式(中缀表达式转后缀表达式)


逆波兰表达式又叫做后缀表达式。在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,这种表示法也称为中缀表示。波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示表达式的方法,按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。


为啥要使用逆波兰表达式


逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式。例如(a+b)(c+d)转换为ab+cd+

去除原来表达式中的括号,因为括号只指示运算顺序,不是实际参与计算的元素。

使得运算顺序有规律可寻,计算机能编写出代码完成计算。


1684813943521.png



实现原理


一、如果输入的是操作数,则直接追加入。当输入的是 运算符 或者 分界符时压入栈中

1. 如果是左括号 ‘(’, 则 直接压入栈中,等待下一个最近的 右括号 与之配对。

2. 如果是右括号 ‘)’,则说明有一对括号已经配对(在表达式输入无误的情况下)。那我们就不将它压栈,而是直接将它丢弃😶‍🌫️,然后出栈,得到元素d,将d依次运算。一直循环,直到出栈元素d 是 左括号 ( ,我们同样丢弃他。

(3)如果是运算符

1.如果栈顶元素不是运算符,则二者没有可比性,则直接将此运算符压栈。 例如栈顶是左括号 ( ,或者栈为空。

2.如果栈顶元素是运算符 ,则比较进入的运算符和栈顶运算符的优先级。如果进入运算符 > 栈顶运算符 ,则直接将此运算符压栈。

如果不满足,则将栈顶运算符出栈,再试图将运算符压栈,如果如果依然不满足 ,继续将新的栈顶运算符弹出 ,直到该运算符可以压入栈中为止。

也就是说,如果在栈中,有2个相邻的元素都是运算符,则他们必须满足:下层运算符的优先级一定小于上层元素的优先级,才能相邻。

最后,如果栈中还有元素,则依次弹出追加到d后,就得到了后缀表达式。


代码实现


#include <ctype.h>
#define STACK_INIT_SIZE 20 //栈的初始化空间大小
#define STACKINCREMENT        10 //每次增加的栈空间大小
#define MAXBUFFER                10 //缓冲区大小
typedef double ElemType;
typedef struct
{
    int stackSize;
    ElemType* base;//指向栈底的指针
    ElemType* top;//指向栈顶的指针
}sqStack;
void initStack(sqStack* s)
{
    s->base = (ElemType*)malloc(STACK_INIT_SIZE * sizeof(ElemType));
    if (!s->base)
    {
        exit(0);
    }
    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
}
void Push(sqStack* s, ElemType e)//进栈
{
    if (s->top - s->base >= s->stackSize)
    {
        s->base = (ElemType*)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
        if (!s->base)
        {
            exit(0);
        }
        s->top = s->base + s->stackSize;
        s->stackSize = s->stackSize + STACK_INIT_SIZE;//大小要更新,不然会误认为容量不够又继续申请内存
    }
    *(s->top) = e;//把e对应的元素压入栈顶
    s->top++;//栈顶上移
}
void Pop(sqStack* s, ElemType* e)//出栈
{
    if (s->top == s->base)//判断栈是不是空
    {
        return;
    }
    *e = *--(s->top);
}
int stackLen(sqStack s)//计算栈的当前容量,s.stackSize
{
    return (s.top - s.base);
}
int main()
{
    char c;
    sqStack s;
    int i = 0;
    double d, e;//临时变量
    char str[MAXBUFFER];//小数存放缓冲区
    initStack(&s);
    printf("请输入逆波兰表达式,数据与运算符之间用空格隔开,按#键结束!\n");
    scanf("%c", &c);
    while (c != '#')
    {
        while (isdigit(c) || c == '.')//判断c是数字,头文件ctype.h,过滤数字和点以外的数据
        {
            str[i++] = c;
            str[i] = '\0';//给不知道下一个是否还要字符的位置赋结束符,以免不知道多长
            if (i >= MAXBUFFER)//字符串溢出
            {
                printf("\n出错:输入的单个数据过长!\n");
                return -1;
            }
            scanf("%c", &c);//继续输入数据
            if (c == ' ')//输入空格,代表单个数据输入结束
            {
                d = atof(str);//把字符串str转化为浮点数
                Push(&s, d);
                i = 0;//i初始化,进入下一个循环
                break;
            }
        }
        switch (c)
        {
        case '+'://两个出栈,并相加
            Pop(&s, &e);//出栈第一个数,给e
            Pop(&s, &d);//出栈第二个数,给d
            Push(&s, d + e);
            break;
        case '-'://两个出栈,并相减
            Pop(&s, &e);//出栈第一个数,给e
            Pop(&s, &d);//出栈第二个数,给d
            Push(&s, d - e);
            break;
        case '*'://两个出栈,并相乘
            Pop(&s, &e);//出栈第一个数,给e
            Pop(&s, &d);//出栈第二个数,给d
            Push(&s, d * e);
            break;
        case '/'://两个出栈,并相除
            Pop(&s, &e);//出栈第一个数,给e
            Pop(&s, &d);//出栈第二个数,给d
            if (e != 0)
            {
                Push(&s, d / e);
            }
            else
            {
                printf("\n错误:除数为零!\n");
                return -1;
            }
            break;
        }
        scanf("%c", &c);
    }
    Pop(&s, &d);//最终的结算结果放在d里
    printf("计算结果为:%f\n", d);
    return 0;
}
目录
相关文章
|
7天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的优化算法及其应用
本文旨在探讨深度学习中常用的优化算法,包括梯度下降、动量方法、AdaGrad、RMSProp和Adam等。通过分析每种算法的原理、优缺点及适用场景,揭示它们在训练深度神经网络过程中的关键作用。同时,结合具体实例展示这些优化算法在实际应用中的效果,为读者提供选择合适优化算法的参考依据。
|
8天前
|
算法 前端开发 机器人
一文了解分而治之和动态规则算法在前端中的应用
该文章详细介绍了分而治之策略和动态规划算法在前端开发中的应用,并通过具体的例子和LeetCode题目解析来说明这两种算法的特点及使用场景。
一文了解分而治之和动态规则算法在前端中的应用
|
14天前
|
算法 调度
贪心算法基本概念与应用场景
尽管贪心算法在许多问题中都非常有效,但它并不总是会产生最优解。因此,在应用贪心算法前,重要的是先分析问题是否适合采用贪心策略。一些问题可能需要通过动态规划或回溯等其他算法来解决,以找到确切的全局最优解。
47 1
WK
|
17天前
|
机器学习/深度学习 算法 数据挖掘
PSO算法的应用场景有哪些
粒子群优化算法(PSO)因其实现简单、高效灵活,在众多领域广泛应用。其主要场景包括:神经网络训练、工程设计、电力系统经济调度与配电网络重构、数据挖掘中的聚类与分类、控制工程中的参数整定、机器人路径规划、图像处理、生物信息学及物流配送和交通管理等。PSO能处理复杂优化问题,快速找到全局最优解或近似解,展现出强大的应用潜力。
WK
20 1
|
26天前
|
机器学习/深度学习 算法 Python
群智能算法:深入解读人工水母算法:原理、实现与应用
近年来,受自然界生物行为启发的优化算法备受关注。人工水母算法(AJSA)模拟水母在海洋中寻找食物的行为,是一种新颖的优化技术。本文详细解读其原理及实现步骤,并提供代码示例,帮助读者理解这一算法。在多模态、非线性优化问题中,AJSA表现出色,具有广泛应用前景。
|
8天前
|
算法 前端开发
一文了解贪心算法和回溯算法在前端中的应用
该文章深入讲解了贪心算法与回溯算法的原理及其在前端开发中的具体应用,并通过分析LeetCode题目来展示这两种算法的解题思路与实现方法。
|
1月前
|
机器学习/深度学习 算法 数据挖掘
R语言中的支持向量机(SVM)与K最近邻(KNN)算法实现与应用
【9月更文挑战第2天】无论是支持向量机还是K最近邻算法,都是机器学习中非常重要的分类算法。它们在R语言中的实现相对简单,但各有其优缺点和适用场景。在实际应用中,应根据数据的特性、任务的需求以及计算资源的限制来选择合适的算法。通过不断地实践和探索,我们可以更好地掌握这些算法并应用到实际的数据分析和机器学习任务中。
|
2月前
|
算法 C++
A : DS串应用–KMP算法
这篇文章提供了KMP算法的C++实现,包括计算模式串的next数组和在主串中查找模式串位置的函数,用于演示KMP算法的基本应用。
|
2月前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
82 1
|
2月前
|
数据采集 搜索推荐 算法
【高手进阶】Java排序算法:从零到精通——揭秘冒泡、快速、归并排序的原理与实战应用,让你的代码效率飙升!
【8月更文挑战第21天】Java排序算法是编程基础的重要部分,在算法设计与分析及实际开发中不可或缺。本文介绍内部排序算法,包括简单的冒泡排序及其逐步优化至高效的快速排序和稳定的归并排序,并提供了每种算法的Java实现示例。此外,还探讨了排序算法在电子商务、搜索引擎和数据分析等领域的广泛应用,帮助读者更好地理解和应用这些算法。
25 0
下一篇
无影云桌面