非递归方法实现递归(使用俩个例子, C语言), 理解程序就是一个状态机

非递归方法实现递归

要达到这个目的, 首先需要理解什么是程序. 程序就是一些机器指令序列. 只需要顺序执行的简单语句和条件跳转语句, 就能实现任何程序, 所以想象这些指令序列都有一个帧栈维护, 每一帧都有一个pc值. 所以可以进行如下抽象

  • 函数执行: 取出top的pc值, 执行对应的简单指令
  • 函数调用: 往帧栈中push新的帧
  • 函数返回: pop帧栈的一帧
    在这里插入图片描述

例1: 非递归汉诺塔实现

递归实现汉诺塔可以参考递归练习-汉诺塔

// hanoi-r.c
void hanoi(int n, char from, char to, char via)
{
    if (n == 1) {
        printf("%c -> %c\n", from, to);
    }
    else {
        hanoi(n - 1, from, via, to);
        hanoi(1, from, to, via);
        hanoi(n - 1, via, to, from);
    }
}

如上图原理所示, 非递归的汉诺塔实现如下:

// hanoi-nr.c
typedef struct {
  int pc, n;
  char from, to, via;
} Frame;

//函数执行, push, 新push的函数总是pc等于0开始, 后续按顺序执行
#define call(...) ({ *(++top) = (Frame) { .pc = 0, __VA_ARGS__ }; }) 
//函数返回就是pop, 所以top--
#define ret()     ({ top--; }) 
//简单的跳转指令
#define goto(loc) ({ f->pc = (loc) - 1; })

void hanoi(int n, char from, char to, char via) {
  Frame stk[64], *top = stk - 1; // 定义帧栈, 和栈顶
  call(n, from, to, via);
  // hanoi函数总是按顺序执行, 所以pc的值一直++, 知道帧栈为空
  for (Frame *f; (f = top) >= stk; f->pc++) {
    n = f->n; from = f->from; to = f->to; via = f->via;
    switch (f->pc) {
      case 0: if (n == 1) { printf("%c -> %c\n", from, to); goto(4); } break;
      // goto(4)表示, 直接执行ret(), 即函数返回
      case 1: call(n - 1, from, via, to);   break;
      case 2: call(    1, from, to,  via);  break;
      case 3: call(n - 1, via,  to,  from); break;
      case 4: ret();                        break;
      default: assert(0);
    }
  }
}

例2: f函数调用g函数, g函数调用f函数.

递归实现为:

// fg-r.c
int g(int);

int f(int n)
{
    if (n <= 1)
        return 1;
    return f(n - 1) + g(n - 2);
}

int g(int n)
{
    if (n <= 1)
        return 1;
    return f(n + 1) + g(n - 1);
}

可以自己先思考一下
小提示:
将pc值分为两种, 一种f函数的pc值, 一种g函数的pc值.
在这里插入图片描述
所以函数调用的时候, 要判断取出的frame是f函数还是g函数, 这里我采用的方法是添加一个成员变量func.

typedef struct {
    int fpc, gpc;
    char func;	// 判断是f函数还是g函数
    int n;
} Frame;
// 宏定义与上例类似
#define call(...) ({ *(++top) = (Frame) { .fpc = 0, .gpc = 0, __VA_ARGS__ }; }) 
#define ret()     ({ top--; }) 
#define f_goto(loc) ({ f->fpc = (loc) - 1; })
#define g_goto(loc) ({ f->gpc = (loc) - 1; })

函数主体部分:

void stackFrame(int n, char func, int* res) {
    Frame stk[32], *top = stk - 1;
    call(func, n);
    for (Frame *f; (f = top) >= stk;) {
        int num = f->n;
        if (f->func == 'f') {
            switch(f->fpc) {
                case 0: if(num <= 1) { (*res)++; f_goto(3); } break;
                case 1: call('f', num - 1); break;
                case 2: call('g', num - 2); break;
                case 3: ret(); break;
                default: assert(0);
            }
            f->fpc++; // 如果执行的是f函数, fpc就加一, 反之, gpc加一
        } else if(f->func == 'g') {
            switch(f->gpc) {
                case 0: if(num <= 1) { (*res)++; g_goto(3); } break;
                case 1: call('f', num + 1); break;
                case 2: call('g', num - 1); break;
                case 3: ret(); break;
                default: assert(0);
            }
            f->gpc++;
        }
    }
}

int f_nr(int n) {
    int res = 0;
    stackFrame(n, 'f', &res);
    return res;
}

int g_nr(int n) {
    int res = 0;
    stackFrame(n, 'g', &res);
    return res;
}

测试

#include<stdio.h>

#include"fg-r.c"
#include"fg-nr.c"

int main() {
    printf("%d\n", f(4));
    printf("%d\n", f_nr(4));
    printf("%d\n", g(5));
    printf("%d\n", g_nr(5));
}

输出结果如下
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值