每日一题——逆波兰表达式求值(前缀、中缀、后缀表达式的说明,库函数atoi()的解析)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 每日一题——逆波兰表达式求值(前缀、中缀、后缀表达式的说明,库函数atoi()的解析)

逆波兰表达式求值

题目链接

中缀,前缀(波兰),后缀(逆波兰)表达式的基本概念

  • 逆波兰表达式即所谓的后缀表达式
  • 我们常见的数学表达式如:3 + 4、5 + (6 -3) * 2……等,都是中缀表达式,中缀表达式的操作符位于操作数的中间
  • 中缀表达式:也叫波兰表达式,如- × + 3 4 5 6前缀表达式的操作符位于操作数的前面
  • 后缀表达式:也叫逆波兰表达式,如3 4 + 5 × 6 -后缀表达式的操作符位于操作数的后面

逆波兰表达式的优点和计算方法

优点

  • 省略了括号,使表达式更加简洁明了,减少了歧义。
  • 方便计算机进行计算,因为计算机可以使用栈来实现对后缀表达式的计算,而不需要使用递归或复杂的算法
  • 可以避免运算符优先级的问题,因为后缀表达式中运算符的优先级是通过运算符在表达式中的位置来确定的,而不是通过运算符本身的优先级来确定的。
  • 可以方便地转换成前缀表达式,因为后缀表达式的计算顺序与前缀表达式的计算顺序相反,只需将后缀表达式中的操作数和运算符反转即可得到前缀表达式。

计算方法

  • 适合用栈操作运算遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

思路

函数原型

int evalRPN(char** tokens, int tokensSize)
  • 可能有小伙伴会疑惑,为什么要传入二级指针呢?tokens存储的是数字字符‘0’ ~ ‘9’,和操作符字符‘+’, ‘-’ ,‘*’, ‘/’,我们又不改变数组的值,只是对其进行解引用,为什么要传入二级指针呢?其实,大家可能忽略了一点,即被操作的数字不一定是个位数, 如果被操作的是一个十位数及其以上的数,那么就需要一个字符串来存储这多个数字字符,继而我们可以得到,我们需要用一个字符指针数组来存储这多个字符串
  • tokensSize就是字符串指针数组中字符串的个数

如何将数字入栈

  • 如果被操作数是一个个位数,那么好办,用 StackPush(st, tokens[i][0] - '0')这个操作就好了(StackPush是入栈函数)
  • 但如果被操作数是以一个十位数及其以上的数字呢?那我们就要对多个字符进行处理,如果是一个负数就更麻烦了。那有没有简单的操作呢?
  • 这里我们就需要用到==库函数atoi()==了
库函数atoi()
  • 函数原型 int atoi(const char *str )
  • str即传入的字符串
  • 函数功能:把字符串转换成整型数
  • 返回值:此值由将输入字符作为数字解析而生成。 如果该输入无法转换为该类型的值,则atoi的返回值为 0
  • 注:该函数的返回值是int类型,因此传入的字符串所表示的值不能超过int所能表示的范围
  • EG:
#include <stdio.h>
#include <stdlib.h>
int main()
{
  int a;
  char *ptr1 = "3124";
  char *ptr2 = "0";
  char *ptr3 = "12.33";
  char *ptr4 = "-1245";
  char *ptr5 = "+21";
  char *ptr6 = "s3241";
    char *ptr7 = "*3241";
    char *ptr8 = "/3241";
  a = atoi(ptr1);
  printf("\"%s\"\t-> %d\n",ptr1, a);
  a = atoi(ptr2);
  printf("\"%s\"\t-> %d\n",ptr2, a);
  a = atoi(ptr3);
  printf("\"%s\"\t-> %d\n",ptr3, a);
  a = atoi(ptr4);
  printf("\"%s\"\t-> %d\n",ptr4, a);
  a = atoi(ptr5);
  printf("\"%s\"\t-> %d\n",ptr5, a);
  a = atoi(ptr6);
  printf("\"%s\"\t-> %d\n",ptr6, a);
    a = atoi(ptr7);
  printf("\"%s\"\t-> %d\n",ptr7, a);
    a = atoi(ptr8);
  printf("\"%s\"\t-> %d\n",ptr8, a);
  return 0;
}
  • 可以得到如下结果:

  • 缺陷分析:我们可以看到,如果传入的是无效的字符串,那么返回值就是0,那么在不知道传入的字符串是否有效的情况下,我们就会不知道字符串到底是无效字符串还是本来是是字符串“0”。

实现代码

  • 有了以上的准备,这题就好写了,直接上代码。
#include<assert.h>
typedef struct Stack
{
    int* stack;
    int top;
}ST;
//判断栈空
bool StackEmpty(ST *st)
{
    return st->top == 0;
}
//初始化栈
void StackInit(ST* st, int len)
{
    st->stack = (int*)malloc(sizeof(int) * len);
    st->top = 0;
}
//入栈
void StackPush(ST* st, int val)
{
    st->stack[st->top] = val;
    st->top++;
}
//出栈,并返回栈元素
int StackPop(ST* st)
{
    assert(!StackEmpty(st));
    st->top--;
    return st->stack[st->top];
}
int evalRPN(char** tokens, int tokensSize) {
    ST* st = (ST*)malloc(sizeof(ST)); //申请栈内存
    StackInit(st, tokensSize);  //初始化栈
    for (int i = 0; i < tokensSize; i++)
    {
        //如果是操作符,并且字符串的长度为1(排除负数的情况)
        if (strlen(tokens[i]) == 1 && 
        (tokens[i][0] == '+'|| 
        tokens[i][0] == '-' || 
        tokens[i][0] == '*' || 
        tokens[i][0] == '/'))
        {
            //取出栈顶的两个元素
            int num_1 = StackPop(st);
            int num_2 = StackPop(st);
            switch (tokens[i][0])
            {
            case '+':
                StackPush(st, num_2 + num_1);
                break;
            case '-':
                StackPush(st, num_2 - num_1);
                break;
            case '*':
                StackPush(st, num_2 * num_1);
                break;
            case '/':
                StackPush(st, num_2 / num_1);
                break;
            }
        }
        //如果是操作数
        else
        {
            StackPush(st, atoi(tokens[i]));       //利用atoi库函数,直接将字符串转换为数字,入栈
        }
    }
    //返回最后的结果
    return st->stack[--(st->top)];
}

tips:本篇对库函数atoi()的解析,对atoi()函数解析以及缺陷分析,以及对atoi()、atof()的改造这篇博文多有参考,如果读者想对这个库函数有更深的了解,可以去看看大佬写的博客。


相关文章
|
2月前
|
存储 前端开发 JavaScript
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
本文介绍了JavaScript中作用域的概念,包括全局变量和局部变量的区别,预解析机制(变量提升),以及函数返回值的使用和类型。通过具体示例讲解了变量的作用域、函数的返回值、以及如何通过return关键字从函数中返回数据。
23 1
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
|
1月前
|
SQL Oracle 关系型数据库
SQL整库导出语录:全面解析与高效执行策略
在数据库管理和维护过程中,整库导出是一项常见的需求,无论是为了备份、迁移还是数据分析,掌握如何高效、准确地导出整个数据库至关重要
|
1月前
|
存储
atoi函数解析以及自定义类型经典练习题
atoi函数解析以及自定义类型经典练习题
33 0
|
1月前
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
深入探索:Python中的并发编程新纪元——协程与异步函数解析
26 3
|
1月前
|
机器学习/深度学习 算法 C语言
【Python】Math--数学函数(详细附解析~)
【Python】Math--数学函数(详细附解析~)
|
2月前
|
XML JSON 网络协议
超级好用的C++实用库之字节流解析器
超级好用的C++实用库之字节流解析器
28 3
|
2月前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
在网络数据的海洋中,网络爬虫遵循HTTP协议,穿梭于互联网各处,收集宝贵信息。本文将从零开始,使用Python的requests库,深入解析HTTP协议,助你构建自己的网络爬虫帝国。首先介绍HTTP协议基础,包括请求与响应结构;然后详细介绍requests库的安装与使用,演示如何发送GET和POST请求并处理响应;最后概述爬虫构建流程及挑战,帮助你逐步掌握核心技术,畅游数据海洋。
67 3
|
2月前
|
缓存 网络协议 分布式数据库
超级好用的C++实用库之DNS解析
超级好用的C++实用库之DNS解析
50 0
|
2月前
|
设计模式 存储 算法
PHP中的设计模式:策略模式的深入解析与应用在软件开发的浩瀚海洋中,PHP以其独特的魅力和强大的功能吸引了无数开发者。作为一门历史悠久且广泛应用的编程语言,PHP不仅拥有丰富的内置函数和扩展库,还支持面向对象编程(OOP),为开发者提供了灵活而强大的工具集。在PHP的众多特性中,设计模式的应用尤为引人注目,它们如同精雕细琢的宝石,镶嵌在代码的肌理之中,让程序更加优雅、高效且易于维护。今天,我们就来深入探讨PHP中使用频率颇高的一种设计模式——策略模式。
本文旨在深入探讨PHP中的策略模式,从定义到实现,再到应用场景,全面剖析其在PHP编程中的应用价值。策略模式作为一种行为型设计模式,允许在运行时根据不同情况选择不同的算法或行为,极大地提高了代码的灵活性和可维护性。通过实例分析,本文将展示如何在PHP项目中有效利用策略模式来解决实际问题,并提升代码质量。
|
3月前
|
SQL 数据处理 数据库

推荐镜像

更多