【经典算法实现 15】阿克曼函数 -- 非递归实现
在前面《【经典算法实现 14】阿克曼函数(手动推导求解、递归实现、非递归实现)》 中,
我们实现了阿克曼函数的手动推导及递归实现代码,
在本文中,我们来研究下它的非递归实现。
其递归代码为:
long ack(int m,int n)
{
if(m == 0){
return n+1;
}else if(n == 0){
return ack(m - 1, 1);
}else{
return ack(m - 1, ack(m, n - 1));
}
}
一、阿克曼函数 – 非递归实现
由于阿克曼函数中,每计算一个数,其都高度依赖前一个数计算的结果,
因此在本非递归实现的思路,其实和递归差不多,利用栈来实现。
由于在C 语言中,操作栈比较麻烦,我们此处,就用单向循环链表来模拟栈。
以链表的头指针作为栈顶, 每次入栈就是向链表头部插入一个元素,出栈就是从链表头部删除一个元素
具体实现如下:
#include <stdio.h>
// 单向循环链表定义
typedef struct ListNode{
long long value;
struct ListNode *next;
}ListNode, *LinkList;
// 定义两个List 分别存储 m 和 n 的值.
LinkList m_ListHead;
LinkList n_ListHead;
int is_Empty(LinkList ListHead){
/*
int count=0;
LinkList List_tmp=ListHead->next;
while(List_tmp != ListHead){
count++;
List_tmp = List_tmp->next;
}
printf("当前链表元素个数 %d\n",count);
*/
// 单向循环链表next 等于自身时,为空
if(ListHead->next == ListHead){
return 1;
}
return 0;
}
// 入栈,使用头插法插入元素
void push(LinkList ListHead, long long num){
LinkList List_tmp;
List_tmp = (LinkList)malloc(sizeof(struct ListNode));
List_tmp->value = num;
List_tmp->next = ListHead->next;
ListHead->next = List_tmp;
}
// 出栈, 从头部删除元素
void pop(LinkList ListHead){
LinkList List_tmp;
if( !is_Empty(ListHead) ){
List_tmp = ListHead->next;
ListHead->next = List_tmp->next;
free(List_tmp); // 释放元素
}
}
void clear_List(LinkList ListHead){
int count=0;
LinkList List_tmp=ListHead->next;
while(List_tmp != ListHead){
ListHead->next = List_tmp->next;
free(List_tmp);
List_tmp = ListHead->next;
count++;
}
printf("\n释放了%d个链表元素!!!\n", count);
}
// 阿克曼函数实现
long long ack(long long m, long long n)
{
push(m_ListHead, m); // 将 m 入栈
push(n_ListHead, n); // 将 n 入栈
while( !is_Empty(m_ListHead) ) // 判断 m 链表是否为空
{
// m != 0时,入栈
while(m != 0){
if(n == 0){
printf("[%s][%4d] 当前m=%lld, n=%lld, 即将入栈m-1=%lld, n=1 \n", __func__, __LINE__, m, n, m-1);
m = m-1;
n = 1;
push(m_ListHead, m); // 将 m 入栈
push(n_ListHead, n); // 将 n 入栈
}else{
// 使用 -1 代表 ack(m - 1, ack(m, n - 1)); 的情况
printf("[%s][%4d] 当前m=%lld, n=%lld, 即将入栈m-1=%lld, n=-1 \n", __func__, __LINE__, m, n, m-1);
n = n-1;
push(m_ListHead, m-1); // 将 m 入栈
push(n_ListHead, -1); // 将 n 入栈
}
}
// 当 m==0 时 出栈
printf("[%s][%4d] 当前m=%lld, n=%lld, 计算 n=n+1 = %lld\n", __func__, __LINE__, m, n, n+1);
n = n+1;
while( (!is_Empty(m_ListHead)) && (n_ListHead->next->value != -1)){
printf("[%s][%4d] 当前m=%lld, n=%lld, 即将出栈 m =%lld, n=%lld \n", __func__, __LINE__, m, n, m_ListHead->next->value, n_ListHead->next->value);
pop(m_ListHead);
pop(n_ListHead);
}
// 处理 ack(m - 1, ack(m, n - 1));的情况,此时,获取m-1 的值,弹出-1, 将当前n push 入栈
if((!is_Empty(m_ListHead))){
m = m_ListHead->next->value;
pop(n_ListHead);
push(n_ListHead, n);
}
}
return n;
}
int main(void)
{
long long m=0;
long long n=0;
// 分配 List 头结点
m_ListHead = (LinkList)malloc(sizeof(struct ListNode));
n_ListHead = (LinkList)malloc(sizeof(struct ListNode));
m_ListHead->next = m_ListHead;
n_ListHead->next = n_ListHead;
while(m != -1){
scanf("%d %d", &m, &n);
printf("\n\n计算结果===> m=%lld,n=%lld 时, ack=%lld \n\n", m, n, ack(m, n));
}
// 释放链表头节点内存
free(m_ListHead);
free(n_ListHead);
return 0;
}
实测结果为:
2 2
[ack][ 118] 当前m=2, n=2, 即将入栈m-1=1, n=-1
[ack][ 118] 当前m=2, n=1, 即将入栈m-1=1, n=-1
[ack][ 110] 当前m=2, n=0, 即将入栈m-1=1, n=1
[ack][ 118] 当前m=1, n=1, 即将入栈m-1=0, n=-1
[ack][ 110] 当前m=1, n=0, 即将入栈m-1=0, n=1
[ack][ 125] 当前m=0, n=1, 计算 n=n+1 = 2
[ack][ 129] 当前m=0, n=2, 即将出栈 m =0, n=1
[ack][ 125] 当前m=0, n=2, 计算 n=n+1 = 3
[ack][ 129] 当前m=0, n=3, 即将出栈 m =0, n=2
[ack][ 129] 当前m=0, n=3, 即将出栈 m =1, n=1
[ack][ 118] 当前m=1, n=3, 即将入栈m-1=0, n=-1
[ack][ 118] 当前m=1, n=2, 即将入栈m-1=0, n=-1
[ack][ 118] 当前m=1, n=1, 即将入栈m-1=0, n=-1
[ack][ 110] 当前m=1, n=0, 即将入栈m-1=0, n=1
[ack][ 125] 当前m=0, n=1, 计算 n=n+1 = 2
[ack][ 129] 当前m=0, n=2, 即将出栈 m =0, n=1
[ack][ 125] 当前m=0, n=2, 计算 n=n+1 = 3
[ack][ 129] 当前m=0, n=3, 即将出栈 m =0, n=2
[ack][ 125] 当前m=0, n=3, 计算 n=n+1 = 4
[ack][ 129] 当前m=0, n=4, 即将出栈 m =0, n=3
[ack][ 125] 当前m=0, n=4, 计算 n=n+1 = 5
[ack][ 129] 当前m=0, n=5, 即将出栈 m =0, n=4
[ack][ 129] 当前m=0, n=5, 即将出栈 m =1, n=3
[ack][ 118] 当前m=1, n=5, 即将入栈m-1=0, n=-1
[ack][ 118] 当前m=1, n=4, 即将入栈m-1=0, n=-1
[ack][ 118] 当前m=1, n=3, 即将入栈m-1=0, n=-1
[ack][ 118] 当前m=1, n=2, 即将入栈m-1=0, n=-1
[ack][ 118] 当前m=1, n=1, 即将入栈m-1=0, n=-1
[ack][ 110] 当前m=1, n=0, 即将入栈m-1=0, n=1
[ack][ 125] 当前m=0, n=1, 计算 n=n+1 = 2
[ack][ 129] 当前m=0, n=2, 即将出栈 m =0, n=1
[ack][ 125] 当前m=0, n=2, 计算 n=n+1 = 3
[ack][ 129] 当前m=0, n=3, 即将出栈 m =0, n=2
[ack][ 125] 当前m=0, n=3, 计算 n=n+1 = 4
[ack][ 129] 当前m=0, n=4, 即将出栈 m =0, n=3
[ack][ 125] 当前m=0, n=4, 计算 n=n+1 = 5
[ack][ 129] 当前m=0, n=5, 即将出栈 m =0, n=4
[ack][ 125] 当前m=0, n=5, 计算 n=n+1 = 6
[ack][ 129] 当前m=0, n=6, 即将出栈 m =0, n=5
[ack][ 125] 当前m=0, n=6, 计算 n=n+1 = 7
[ack][ 129] 当前m=0, n=7, 即将出栈 m =0, n=6
[ack][ 129] 当前m=0, n=7, 即将出栈 m =1, n=5
[ack][ 129] 当前m=0, n=7, 即将出栈 m =2, n=2
计算结果===> m=2,n=2 时, ack=7
-1 2
请按任意键继续. . .