代码4:非递归的汉诺塔

Intro:

  • 绪论2:应用视角的操作系统。
  • 目的:改写为非递归的汉诺塔,理解“状态机模型”。

一、递归版汉诺塔

课本上最基础的做法,递归是模拟某一步:把n-1个盘子从from移到via,就能把剩下的一个盘子从from移到to,最后把n-1个盘子从via移到to即可。

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

二、非递归版

非递归版的实现用了栈 Frame[64] 模拟整个调用过程。

typedef struct {
  int pc, n; // 当前计数器、盘子数
  char from, to, via;
} Frame;

// ...和__VA_ARGS__为可变参数列表,具体语法过程为:
// 1. {.pc ...} 代表 struct Frame 的初始化
// 2. 栈顶指针 top 下移,模拟把初始化好的 Frame 压入栈中
#define call(...) ({ *(++top) = (Frame) { .pc = 0, __VA_ARGS__ }; })
#define ret()     ({ top--; }) // 出栈
#define goto(loc) ({ f->pc = (loc) - 1; }) // f->pc 代表了具体执行 swtich 中哪条指令

void hanoi_nr(int n, char from, char to, char via) {
  Frame stk[64], *top = stk - 1;
  call(n, from, to, via); // 初始化
  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;
      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);
    }
  }
}

这段代码的执行过程需要多读两遍。

posted @ 2024-07-07 16:00  7hu95b  阅读(111)  评论(0)    收藏  举报