【经典算法实现 15】阿克曼函数(非递归实现)

【经典算法实现 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
请按任意键继续. . .

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

"小夜猫&小懒虫&小财迷"的男人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值