写点什么

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

作者:清风莫追
  • 2022 年 10 月 08 日
    湖南
  • 本文字数:2663 字

    阅读完需:约 9 分钟


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

1.题目

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

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

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


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


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


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



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


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

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为每次四则运算得到的中间结果。


5.算法分析

算法的时间复杂度应为:,其中 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.运行结果

5.算法分析

算法复杂度为:


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




感谢阅读


博主主页:CSDN清风莫追


发布于: 刚刚阅读数: 4
用户头像

清风莫追

关注

还未添加个人签名 2022.08.09 加入

编程一学生

评论

发布
暂无评论
【算法作业】实验四:逆波兰表达式求值 & Fibonacci数列的尾递归与非递归程序_数据结构_清风莫追_InfoQ写作社区