【经典算法实现 16】阿克曼函数(非递归实现 代码优化)

接着前两篇文章:
【经典算法实现 14】阿克曼函数(手动推导求解、递归实现、非递归实现)
【经典算法实现 15】阿克曼函数 – 非递归实现

本文是对前面代码的优化:

优化思路为:

  1. 为方便理解,将 m 和 n 放在同一个链表中实现,成对push,成对pop
  2. 为方便理解,将代码逻辑与递归函数 结构写在一样,方便对照学习

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
请按任意键继续. . .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小馋喵星人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值