0
这个星期,数据结构与算法的陈老师布置了一道题目,要求用非递归的算法计算阿克曼函数。在百度上找了很多个网址,都没有找到我想要的答案。于是,在我做出来之后,打算与大家分享一下我自己的想法。不足之处,请多多指教!
阿克曼函数的形式
如下图是我从《数据结构与程序设计》翻译版描述的阿克曼函数。
看到阿克曼函数的这个形式,毫无疑问第一个想到的肯定是用递归的算法。但是如果不用递归的算法,事情就变得麻烦起来了。
分析
说到用其它的算法代替递归算法,大部分第一个想到的肯定是使用循环,我第一个想到的也是循环,这是正确的。但是这个问题比较特殊,特殊之处就在于它的问题的出现是线性的。也就是说,你想要解决上一个问题,你只需要解决这一个问题;你想要解决这一个问题,你只需要解决下一个问题。注意我这里的用词:只需要!!!递归的实现其实就是一个堆栈出栈的过程。
下面就以计算A(2,1)的例子分析。如下图。
(注:这个图上的元素应该从下往上看,像堆栈一样)
如图,我们要解决A(2,1),就得解决A(1,A(2,0));要解决A(1,A(2,0)),就得解决A(2,0)… 以此类推,直到我们得到一个m=0的元素A(0,1),A(0,1)解决了之后,它的结果可以一直回到原来要解决的问题A(1,A(2,0)),也即是A(1,3)。往后同样分析,要解决A(1,3),就得先解决A(0,A(1,2));要解决A(0,A(1,2)),就得先解决A(1,2)…直到我们最终推出A(2,1)的结果为止,最终的结果是5。
如何写代码
从上面的分析,我们可以发现:
(1)每一个需要计算的元素是一个个向上地堆栈,直到遇到m=0的情况,此时A(0,n)可以计算出一个整数,之后就是出栈的操作。
(2)A(m,A(m1,m2)),这种情况在栈中是比较特殊的情况,当这里的m=0时,栈就能继续出栈了;但是m≠0的时候,我们还得继续向上堆栈。
解决办法
(1)声明两个栈s1、s2。
s1存储m
s2存储n
(2)遇到A(m,A(m1,m2))这种情况的时候,将n存储为-1,这是为了后面出栈的算法能够识别特殊情况,并且做出相应的处理。
(3)声明m和n,用于动态存储并操作m值和n值,这是因为在(2)的要求下,n可能在栈s2中存为-1,此时存储的值是一个待定值。
代码
#include<iostream>
#include<stack>
using namespace std;
int asker(int m, int n)
{
stack<int> s1;
stack<int> s2;
s1.push(m);
s2.push(n);
while (!s1.empty())
{
while (m != 0)
{
if (n == 0)
{
m = m - 1;
n = 1;
s1.push(m);
s2.push(n);
}
else
{
n = n - 1;
s1.push(m - 1);
s2.push(-1);
}
}
n = n + 1;
while ((!s1.empty())&& (s2.top() != -1))
{
s1.pop();
s2.pop();
}
if(!s1.empty())
{
m = s1.top();
s2.pop();
s2.push(n);
}
}
return n;
}
void main()
{
cout << asker(2, 1);
}
1
这是我第一次写博客,有很多不足之处。代码是赶出来的,所以会有很多地方使用不当,请大家帮我指出错误!