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

导读:本篇文章讲解 【算法作业】实验四:逆波兰表达式求值 & Fibonacci数列的尾递归与非递归程序,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com


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

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.算法分析

算法的时间复杂度应为:

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.运行结果

在这里插入图片描述

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)。这个问题据说可以用记忆化进行优化,但我还没有实操尝试过。


感谢阅读

博主主页:清风莫追

CSDN话题挑战赛第2期
参赛话题:学习笔记

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/114814.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!