【算法作业】实验四:逆波兰表达式求值 & 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)。这个问题据说可以用记忆化进行优化,但我还没有实操尝试过。

相关文章
|
3月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
100 1
|
8天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。
|
6天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
24 2
|
15天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
18 3
|
28天前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
27天前
|
监控 算法 数据安全/隐私保护
基于三帧差算法的运动目标检测系统FPGA实现,包含testbench和MATLAB辅助验证程序
本项目展示了基于FPGA与MATLAB实现的三帧差算法运动目标检测。使用Vivado 2019.2和MATLAB 2022a开发环境,通过对比连续三帧图像的像素值变化,有效识别运动区域。项目包括完整无水印的运行效果预览、详细中文注释的代码及操作步骤视频,适合学习和研究。
|
1月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
103 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
1月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
20 1
|
20天前
|
缓存 分布式计算 监控
算法优化:提升程序性能的艺术
【10月更文挑战第20天】算法优化:提升程序性能的艺术
|
1月前
|
机器学习/深度学习 算法 数据处理
EM算法对人脸数据降维(机器学习作业06)
本文介绍了使用EM算法对人脸数据进行降维的机器学习作业。首先通过加载ORL人脸数据库,然后分别应用SVD_PCA、MLE_PCA及EM_PCA三种方法实现数据降维,并输出降维后的数据形状。此作业展示了不同PCA变种在人脸数据处理中的应用效果。
31 0