【算法作业】实验四:逆波兰表达式求值 & Fibonacci数列的尾递归与非递归程序

简介: 【算法作业】实验四:逆波兰表达式求值 & Fibonacci数列的尾递归与非递归程序

第一题:逆波兰表达式求值


1.题目

掌握递归的基本语法和思想,利用递归程序实现逆波兰表达式,并分析算法复杂度。


2.问题分析与算法设计思路

这里实现了对多位数整的操作,运算仅包含四则运算。输入中使用.来将不同的操作数隔开,使用@表示表达式的结束。


使用栈的实现方法在另一篇博客中写过,请参考:计算后缀表达式-算法与数据结构-栈的运用-C++语言实现。这里我们将使用递归的方法来实现(虽然也用到了栈,但仅仅是为了从尾部开始读取字符而已)。


思路:

每个操作符将需要两个操作数,可以为表达式构建一颗递归树,每个操作数可能就是表达式中的一个数字,也可能要由一个子表达式(递归就来了)计算得到。


以输入3.5.2.-*7.+@为例,构建递归树:


image.png


注意:

由于我是从表达式的尾部开始读取字符,而减法、除法运算并不满足交换律,因此需要额外调整一下。


遇到问题:

再调试过程中遇到了一个很奇怪的问题,我现在仍没有弄清楚原理,我将它记录在问答社区:有个带函数的表达式前加负号无效。在下面的代码中,采用了避免该问题的写法。


3.算法实现

//逆波兰表达式——递归 
#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
stack<char> s;
int get_num(){//从栈中返回一个整型数字 
  char t=0;
  int sum=0;
  for(int i=1; ! s.empty(); i *= 10){
  t = s.top();
  if('0' <= t && t <= '9'){
    s.pop();
    sum += (t - '0') * i;
  }
  else break;
  }
  //cout<<"sum "<<sum<<endl;
  return sum;
}
int nbl(char x){//对逆波兰表达式求值 
  if(x == '+' || x == '-' || x == '*' || x == '/'){
  s.pop();
  if(x == '+'){
    int t = nbl(s.top()) + nbl(s.top());
    cout<<"t: "<<t<<endl;
    return t;
  }
  else if(x == '-'){
    int t = (nbl(s.top()) - nbl(s.top()));
    t = -t;
    cout<<"t: "<<t<<endl;
    return t;
  }
  else if(x == '*'){
    int t = nbl(s.top()) * nbl(s.top());
    cout<<"t: "<<t<<endl;
    return t;
  }
  else if(x == '/'){
    int t = (int)(1 / ((double)nbl(s.top()) / nbl(s.top())));
    cout<<"t: "<<t<<endl;
    return t;
  }
  }
  else if(x == '.'){
  s.pop();
  return get_num();
  }
}
int main(){
  char t='0';
  while(true){
  cin >> t;
  if(t == '@') break;
  s.push(t);
  }
  if(s.empty()){
  cout<<"表达式为空"<<endl;
  }
  else cout<<nbl(s.top());
  return 0;
}
/*
测试数据
3.5.2.-*7.+@
10.28.30./*7.-6.+@
1000.1000./5.-6.*@
*/

代码更新(9月19日):

改变了函数nbl()中不同四则运算分支的代码写法,这样看起来更加简洁和明确,调试时查看中间结果也更加的方便。


//逆波兰表达式——递归 
#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
stack<char> s;
int get_num(){//从栈中返回一个整型数字 
  char t=0;
  int sum=0;
  for(int i=1; ! s.empty(); i *= 10){
  t = s.top();
  if('0' <= t && t <= '9'){
    s.pop();
    sum += (t - '0') * i;
  }
  else break;
  }
  return sum;
}
int nbl(char x){//对逆波兰表达式求值 
  s.pop(); 
  if(x == '+' || x == '-' || x == '*' || x == '/'){
  if(x == '+'){
    int a = nbl(s.top());
    int b = nbl(s.top());
    return a + b;
  }
  else if(x == '-'){
    int a = nbl(s.top());
    int b = nbl(s.top());
    return b - a;
  }
  else if(x == '*'){
    int a = nbl(s.top());
    int b = nbl(s.top());
    return a * b;
  }
  else if(x == '/'){
    int a = nbl(s.top());
    int b = nbl(s.top());
    return b / a;
  }
  }
  else if(x == '.'){
  return get_num();
  }
}
int main(){
  char t='0';
  while(true){
  cin >> t;
  if(t == '@') break;
  s.push(t);
  }
  if(s.empty()){
  cout<<"表达式为空"<<endl;
  }
  else cout<<nbl(s.top());
  return 0;
}

4.运行结果

输出的t为每次四则运算得到的中间结果。


image.png


5.算法分析

算法的时间复杂度应为:o ( n ) o(n)o(n),其中 n 为输入表达式的长度。


第二题:Fibonacci数列的尾递归与非递归程序


1.题目

将Fibonacci数列的递归求解方法转为尾递归求解;借助栈实现递归转非递归程序。


2.问题分析与算法设计思路

主要是学会可递归问题的多种写法。


3.算法实现

一、尾递归

关于使用尾递归,可以参考一下:尾递归实现斐波那契数


//斐波那契尾递归实现
#include<iostream>
using namespace std;
int fbnq(int n, int a, int b){ //b表示当前值,a表示前一项的值。其实类似于循环迭代,n控制循环次数 
  if(n == 1) return b;
  return fbnq(n-1, b, a+b);
}
int main(){
  int n = 0;
  cout<<"求斐波那契数列的第多少项?"<<endl;
  while(n < 1){
  cin >> n;
  }
  cout<<fbnq(n, 0, 1);
  return 0;
}


二、使用栈的非递归


//斐波那契使用栈非递归实现 
#include<iostream>
#include<stack>
using namespace std;
stack<int> s;
int main(){
  int n = 0;
  int a = 0;
  int b = 0;
  cout<<"求斐波那契数列的第多少项?"<<endl;
  while(n < 1){
  cin >> n;
  }
  s.push(0);
  s.push(1);
  for(int i = 0; i < n - 1; i++){
  a = s.top();
  s.pop();
  b = s.top();
  s.pop();
  s.push(a);
  s.push(a+b);
  }
  b = s.top();
  cout<<b;
  return 0;
}

4.运行结果

image.png


5.算法分析

算法复杂度为:o ( n ) o(n)o(n)。


这里的递归写法时间开销要比非递归大一些(即使不考虑递归操作本身的原因),因为会存在重复计算。例如递归计算f ( 5 ) f(5)f(5),则需要计算f ( 4 ) f(4)f(4)和f ( 3 ) f(3)f(3),而计算f ( 4 ) f(4)f(4)中又计算了一次f ( 3 ) f(3)f(3)。这个问题据说可以用记忆化进行优化,但我还没有实操尝试过。

相关文章
|
8月前
|
机器学习/深度学习 算法 Java
基于灰狼优化算法(GWO)解决柔性作业车间调度问题(Matlab代码实现)
基于灰狼优化算法(GWO)解决柔性作业车间调度问题(Matlab代码实现)
411 1
|
8月前
|
供应链 算法 Java
【柔性作业车间调度问题FJSP】基于非支配排序的多目标小龙虾优化算法求解柔性作业车间调度问题FJSP研究(Matlab代码实现)
【柔性作业车间调度问题FJSP】基于非支配排序的多目标小龙虾优化算法求解柔性作业车间调度问题FJSP研究(Matlab代码实现)
328 1
|
8月前
|
供应链 算法 调度
基于非支配吸血水蛭优化算法 (NSBSLO)求解多目标柔性作业车间调度问题(FJSP)研究(Matlab代码实现)
基于非支配吸血水蛭优化算法 (NSBSLO)求解多目标柔性作业车间调度问题(FJSP)研究(Matlab代码实现)
210 8
|
8月前
|
机器学习/深度学习 负载均衡 算法
【柔性作业车间调度】基于四种多目标优化算法(NSOOA、NSPSO、NSDBO、NSCOA)求解柔性作业车间调度问题FJSP研究(Matlab代码实现)
【柔性作业车间调度】基于四种多目标优化算法(NSOOA、NSPSO、NSDBO、NSCOA)求解柔性作业车间调度问题FJSP研究(Matlab代码实现)
500 0
|
8月前
|
存储 算法 生物认证
基于Zhang-Suen算法的图像细化处理FPGA实现,包含testbench和matlab验证程序
本项目基于Zhang-Suen算法实现图像细化处理,支持FPGA与MATLAB双平台验证。通过对比,FPGA细化效果与MATLAB一致,可有效减少图像数据量,便于后续识别与矢量化处理。算法适用于字符识别、指纹识别等领域,配套完整仿真代码及操作说明。
|
11月前
|
PyTorch 算法框架/工具 C++
人工智能算法python程序运行环境安装步骤整理
本教程详细介绍Python与AI开发环境的配置步骤,涵盖软件下载、VS2017安装、Anaconda配置、PyCharm设置及组件安装等内容,适用于Windows系统,助你快速搭建开发环境。
|
存储 算法 Java
算法系列之递归反转单链表
递归反转链表的基本思路是将当前节点的next指针指向前一个节点,然后递归地对下一个节点进行同样的操作。递归的核心思想是将问题分解为更小的子问题,直到达到基本情况(通常是链表末尾)。
507 5
算法系列之递归反转单链表
|
算法 数据可视化 调度
基于NSGAII的的柔性作业调度优化算法MATLAB仿真,仿真输出甘特图
本程序基于NSGA-II算法实现柔性作业调度优化,适用于多目标优化场景(如最小化完工时间、延期、机器负载及能耗)。核心代码完成任务分配与甘特图绘制,支持MATLAB 2022A运行。算法通过初始化种群、遗传操作和选择策略迭代优化调度方案,最终输出包含完工时间、延期、机器负载和能耗等关键指标的可视化结果,为制造业生产计划提供科学依据。
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
613 1
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。

热门文章

最新文章