接着前两篇文章:
《【经典算法实现 14】阿克曼函数(手动推导求解、递归实现、非递归实现)》
《【经典算法实现 15】阿克曼函数 – 非递归实现》
本文是对前面代码的优化:
优化思路为:
- 为方便理解,将 m 和 n 放在同一个链表中实现,成对push,成对pop
- 为方便理解,将代码逻辑与递归函数 结构写在一样,方便对照学习
PS:
其实代码逻辑与递归一样,运行效率也一样,唯一不同的是,递归是由系统自动实现入栈出栈,
而我们当前代码是由程序主动来模拟入栈出栈,
主要目的还是练手,实际肯定用递归,代码简洁方便,也不容易出错。
代码如下:
#include <stdio.h>
// 定义单向循环链表结构体
typedef struct ListNode{
long long m;
long long n;
struct ListNode * next;
}ListNode, *LinkList;
// 判断链表是否为空
int is_Empty(LinkList ListHead){
if(ListHead->next == ListHead){
return 1;
}
return 0;
}
// 入栈
void push(LinkList ListHead, long long m, long long n){
LinkList ListTmp;
printf("入栈 ---> m=%lld, n=%lld \n", m, n);
ListTmp = (LinkList) malloc(sizeof(ListNode));
ListTmp->m = m;
ListTmp->n = n;
ListTmp->next = ListHead->next;
ListHead->next = ListTmp;
}
// 出栈
void pop(LinkList ListHead){
LinkList ListTmp;
printf("出栈 <--- m=%lld, n=%lld \n", ListHead->next->m, ListHead->next->n);
if( !is_Empty(ListHead) ){
ListTmp = ListHead->next;
ListHead->next = ListTmp->next;
free(ListTmp);
}
}
// 阿克曼函数计算
long long ack(LinkList ListHead, long long m, long long n)
{
push(ListHead, m, n); //入栈, m, n
printf("\n入栈计算 ack(%lld, %lld) \n", m, m);
// 当链表不为空时计算,为空时说明计算完毕
while(!( is_Empty(ListHead) )){
if(m == 0){
// 当 m==0 时,开始出栈
n = n + 1;
// 出栈,直到 n == -1
while( (!is_Empty(ListHead)) && (ListHead->next->n!=-1))
pop(ListHead);
// 出栈,如果 n = -1
if( !is_Empty(ListHead) && (ListHead->next->n==-1)){
// 先将 -1 出栈
m = ListHead->next->m;
pop(ListHead);
printf("\n====> 出栈计算 ack(%lld, %lld)\n",m, n);
// 将当前 n 值push 入栈, 也就是计算 ack(m - 1, ack(m, n - 1)) 的情况
push(ListHead, m, n);
}
}
else if(n == 0)
{
m = m-1;
n = 1;
push(ListHead, m, n);
}
else
{
// 针对 ack(m - 1, ack(m, n - 1)) 的情况,入栈 -1
printf("\n====> 入栈计算 ack(%lld, ack(%lld, %lld))\n", m-1, m, n-1);
push(ListHead, m-1, -1);
// 下一轮计算 ack(m, n - 1), m 保持不变, n = n-1
n = n-1;
}
}
return n;
}
int main(void)
{
long long m=0, n=0;
// 申请链表头节点内存
LinkList ListHead = (LinkList) malloc(sizeof(ListNode));
ListHead->next = ListHead;
printf("阿克曼函数计算程序\n\n");
while(m>=0 && n>=0){
printf("请输入要计算的m和n: ");
scanf("%lld %lld", &m, &n);
if(m>=0 && n>=0)
printf("\n计算结果===> m=%lld,n=%lld 时, ack=%lld \n\n", m, n, ack(ListHead, m, n));
}
// 释放头结点内存
free(ListHead);
return 0;
}
运行结果如下:
阿克曼函数计算程序
请输入要计算的m和n: 2 1
入栈 ---> m=2, n=1
入栈计算 ack(2, 2)
====> 入栈计算 ack(1, ack(2, 0))
入栈 ---> m=1, n=-1
入栈 ---> m=1, n=1
====> 入栈计算 ack(0, ack(1, 0))
入栈 ---> m=0, n=-1
入栈 ---> m=0, n=1
出栈 <--- m=0, n=1
出栈 <--- m=0, n=-1
====> 出栈计算 ack(0, 2)
入栈 ---> m=0, n=2
出栈 <--- m=0, n=2
出栈 <--- m=1, n=1
出栈 <--- m=1, n=-1
====> 出栈计算 ack(1, 3)
入栈 ---> m=1, n=3
====> 入栈计算 ack(0, ack(1, 2))
入栈 ---> m=0, n=-1
====> 入栈计算 ack(0, ack(1, 1))
入栈 ---> m=0, n=-1
====> 入栈计算 ack(0, ack(1, 0))
入栈 ---> m=0, n=-1
入栈 ---> m=0, n=1
出栈 <--- m=0, n=1
出栈 <--- m=0, n=-1
====> 出栈计算 ack(0, 2)
入栈 ---> m=0, n=2
出栈 <--- m=0, n=2
出栈 <--- m=0, n=-1
====> 出栈计算 ack(0, 3)
入栈 ---> m=0, n=3
出栈 <--- m=0, n=3
出栈 <--- m=0, n=-1
====> 出栈计算 ack(0, 4)
入栈 ---> m=0, n=4
出栈 <--- m=0, n=4
出栈 <--- m=1, n=3
出栈 <--- m=2, n=1
计算结果===> m=2,n=1 时, ack=5
请输入要计算的m和n: -1 0
请按任意键继续. . .