zoukankan      html  css  js  c++  java
  • 数据结构(九) — 栈的作用之递归与四则运算

    递归的定义

    前面两章我们说了栈的定义与实现,那么接下来我们就开始真正的使用栈,栈的很重要的一个使用方式就是:递归

    那么什么是递归呢?
    我们把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称做递归函数。同时在高级语言中,调用自己和其他函数并没有本质的不同。
    当然,写递归程序最怕的就是陷入永不结束的无穷递归中 ,所以,每个递归定义必须至少有一个条件,满足时递归不再进行,即不再引用自身而是返回值退出。

    说起递归,可能很多朋友会想到迭代,那么选代和递归的区别有那些区别呢?
    1、迭代使用的是循环结构,递归使用的是选择结构。
    2、递归能使程序的结构更清晰、更简洁、更容易让人理解,从而减少读懂代码的时间。但是大量的递归调用会建立函数的副本,会耗费大量的时间和内存。选代则不需要反复调用函数和占用额外的内存。
    因此我们应该视不同情况选择不同的代码实现方式。

    我们讲了这么多递归的内容,和栈有什么关系呢?
    递归调用其实就是使用栈先进后出的特点,然后循环调用自身

    栈的递归的实现

    上面的概念大家看了可能还是不是太懂,接下来用一个斐波那契数列说明一下:说如果兔子在出生两个月后,就有繁殖能力, 一对兔子每个月能生出一对小兔子来。假设所有兔都不死,那么一年以后可以繁殖多少对兔子呢?

    我们拿新出生的一对小兔子分析一下;第一个月小兔子没有繁殖能力,所以还是一对;两个月后,生下一对小兔子数共有两对; 三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力 , 所以一共是三对……依次类推可以列出下表
    在这里插入图片描述
    如果我们用数学函 数来定义就是:
    在这里插入图片描述
    那么代码实现如下:

    public class Fbi{
    
      public static int Fbi(int i) {
        if (i < 2) {
          return i == 0 ? 0 : 1;
        }
        return Fbi(i - 1) + Fbi(i - 2);
      }
    
      public static void main(String[] args) {
        int k = 0;
        for (int i = 0; i <= 12; i++) {
          k = Fbi(i);
        }
        System.out.println("值为:" + k);
      }
    }
    

    最后的结果
    在这里插入图片描述
    所以我们这里分析一下:函数怎么可以自己调用自己?听起来有些难以理解,不过你可以不把一个递归函数中调用自己的函数看作是在调用自己,而就当它是在调另一个函数。只不过,这个函数和自己长得一样而已。
    我们来模拟代码中的 Fbi (i) 函数当 i = 5 的执行过程,如图所示:
    在这里插入图片描述
    简单的说,就是在前行阶段,对于每一层递归,函数的局部变量、参数值以及返回地址都被压入栈中。在退回阶段,位于栈顶的局部变量、 参数值和返回地址被弹出,用于返回调用层次中执行代码的其余部分,也就是恢复了调用的状态。

    栈的四则运算

    后缀表达式

    上面说了栈的应用之一:递归;栈还有个很重要的应用:数学表达式求值

    数学表达式求值主要是指四则运算求值,也就是加减乘除,这个都还是蛮简单的,按照运算规则来就行了;但是四则运算中有个很麻烦的东西:括号,因为括号导致运算的顺序会发生变化,因为这个原因导致计算机很惆怅啊;后来20 世纪 50 年代,波兰逻辑学家想到了一种不需要括号的后缀表达法,我们也把它称为逆波兰 (Reverse Polish Notation, RPN) 表示
    我们先来看看,对于如“ (3-1) X3+10÷2 " ,如果要用后缀表示法应该是什么样子: " 9 3 1 - 3 * + 10 2 / + " ,这样的表达式称为后缀表达式,叫后缀的原因在于所有的符号都是在要运算数字的后面出现。显然,这里没有了括号。对于从来没有接触过后缀表达式的朋友来讲,这样的表述是很难受的。不过你不喜欢,我们的计算机是非常喜欢的。

    后缀表达式计算结果

    为了解释后缀表达式的好处,我们先来看看,计算机如何应用后缀表达式计算出最终的结果的。
    后缀表达式: 9 3 1 - 3 * + 10 2 / +
    规则:从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果。

    1. 初始化一个空栈,此栈用来对要运算的数字进出使用。如下图左边所示。
    2. 后缀表达式中前三个都是数字,所以 9、 3、 1 进栈,如下图右边所示。
      在这里插入图片描述
    3. 接下来是" - ",所以将栈中的 1 出栈作为减数, 3 出栈作为被减数,并运算 3 - 1 得到 2 ,再将 2 进栈,如下图左边所示。
    4. 接着是数字 3 进栈, 如下图右边所示。
      在这里插入图片描述
    5. 后面是" * ",也就意味着栈中 3 和 2 出栈, 2 与 3 相乘,得到 6,并将 6 进栈,如下图的左边所示。
    6. 下面是" + ",所以栈中 6 和 9 出栈, 9 与 6 相加,得到 15,将 15 进栈,如下图的右边所示。
      在这里插入图片描述
    7. 接着是 10 与 2 两数字进栈,如下图的左边所示。
    8. 接下来是符号 " / ", 因此,栈顶的 2 与 10 出栈, 10 与 2 相除, 得到 5,将 5 进栈,如下图的右边所示。
      在这里插入图片描述
    9. 最后一个是符号" + ",所以 15 与 5 出栈并相加,得到 20,将 20 进栈,如下图的左边所示。
    10. 结果是 20 出栈,栈变为空,如下图的右边所示。
      在这里插入图片描述

    果然,后缀表达法可以很顺利解决计算的问题。但是,就是这个后缀表达式 "9 3 1 - 3 * + 10 2 / + " 是怎么出来的?这个问题不搞清楚,等于没有解决。所以下面,我们就来推导如何让 " 9+ (3-1) X 3+10÷2 " 转化为 " 9 3 1 - 3 * + 10 2 / + "。

    中缀表达式

    我们把平时所用的标准四则运算表达式 ,即 " 9+ (3-1) X3+10÷2 " 叫做中缀表达式。因为所有的运算符号都在两数字的中间,现在我们的问题就是中缀到后缀的转化。

    中缀表达式 " 9+(3-1) X 3+10÷2 " 转化为后缀表达式 " 9 3 1 - 3 * + 10 2 / + "。
    规则 : 从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。

    1. 初始化一空栈,用来对符号进出栈使用。如下图的左图所示。
    2. 第一个字符是数字 9,输出 9,后面是符号" + ",进栈。如下图的右图所示。
      在这里插入图片描述
    3. 第三个字符是" ( ”,依然是括号,因其只是左括号,还未配对,故进栈。如下图的左图所示。
    4. 第四个字符是数字 3,输出,总表达式为 9 3,接着是" - “进栈。 如下图的右图所示。
      在这里插入图片描述
    5. 接下来是数字 1,输出,总表达式为 9 3 1,后面是符号" ) " ,此时,我们需要去匹配此前的" ( " ,所以栈顶依次出栈,并输出,直到" ( “出栈为止。此时左括号上方只有” - “,因此输出” - ”总的输出表达式为 9 3 1 - 。如下图的左困所示。
    6. 接着是数字 3 , 输出,总的表达式为 9 3 1 - 3。 紧接着是符号“ x ”因为此时的栈顶符号为" + “号,优先级低于“ x ”,因此不输出,” * "进栈。如下图的右图所示。
      在这里插入图片描述
    7. 之后是符号 ” + “,此时当前栈顶元素” * " 比这个" + " 的优先级高,因此栈中元素出栈并输出(没有比 " + " 号更低的优先级,所以全部出栈 ,总输出表达式为 9 3 1 - 3 * +。然后将当前这个符号 ” + " 进栈。也就是说,前 6 张图的栈底的" + “是指中缀表达式中开头的 9 后面那个” + " , 而下图左图中的栈底(也是栈顶)的" + "是指 " 9+ (3- 1) X3+ " 中的最后一个 " +“。
    8. 紧接着数字 10,输出,总表达式变为 9 3 1 - 3 * + 10。后是符号" ÷ ",所以 ” / " 进钱。如下图的右图所示。
      在这里插入图片描述
    9. 最后一个数字 2 ,输出,总的表达式为 9 3 1 - 3 * + 10 2。如下图的左图所示。
    10. 因已经到最后,所以将栈中符号全部出栈并输出。最终输出的后缀表达式结果为 9 3 1 - 3 * + 10 2 / +。如下图右图所示。
      在这里插入图片描述

    从刚才的推导中你会发现, 要想让计算机具有处理我们通常的标准(中缀)表达 式的能力,最重要的就是两步:
    1、将中缀表达式转化为后缀表达式(栈用来进出运算的符号)。
    2、将后缀表达式进行运算得出结果(栈用来进出运算的数字)。

  • 相关阅读:
    SQL行转列问题
    pgAdmin III 单表数据的导出导入
    window 服务的安装和卸载
    将Excel表格转成DataTable
    “Timeout 时间已到。在操作完成之前超时时间已过或服务器未响应”解决方法
    form-data提交
    由于本公司项目需要,现急需拥有微软MCSE证书的人才,一经录用,待遇从优!
    Head First设计模式悟道
    entityframwork
    .net 开源模板引擎jntemplate 教程:基础篇之在ASP.NET MVC中使用Jntemplate
  • 原文地址:https://www.cnblogs.com/ZWOLF/p/10859280.html
Copyright © 2011-2022 走看看