汉诺塔的非递归算法

思路

模拟递归程序执行过程,借助一个堆栈,把递归转成非递归算法。

转化过程

1. 递归算法

   

1 void hanoi(int n, char from, char pass, char to) {
2         if (n == 0)
3                 return;
4 
5         hanoi(n - 1, from, to, pass);
6         move(n, from, to);
7         hanoi(n - 1, pass, from, to);
8 }

 

2. 处理首递归

   本函数第2行是结束条件,第5行开始进入首递归。执行第5行函数调用之前,需要保留调用现场,本例中是4个参数入栈,使用新的参数调用hanoi函数。而继续跟踪被调用的函数,可以看出需要一直进行入栈操作,直到参数n == 0为止。 对此行为,我们可以用一个循环来模拟:

  

伪代码:
int
i = n; while (i != 0) {
 push(i);
i --; }

  现在可以断言 i ==0 ,满足函数返回的条件。当函数返回后,需要通过pop操作来恢复现场,然后继续执行后面的语句。为了简化问题,我们假设后面只有move()一条语句,执行完毕该语句后就继续向上一层回溯,直至最顶层, 这样又可以用一个循环来模拟:

伪代码:
int i = n;
while (i != 0) {
 push(i);
  i --;
}
while (栈不空) {
  int m = pop();
  move(m, ...);
  尾递归...
}

 

3. 处理尾递归

   一般而言,尾递归可以直接改成上一条语句的循环。但在本例中,尾递归被嵌在另一个循环中,此时需要模拟它的行为:进入尾递归hanoi()函数后,需要执行其函数体,而函数体又是上述代码中的第一句话,可以如下表示:

伪代码:

a_new_beginning: 
    int i = n;
    while (i != 0) {
     push(i);
      i --;
    }
    while (栈不空) {
      int m = pop();
      move(m, ...);

      产生新的参数,跳出循环,跳转到a_new_beginning语句处;
    }

  相比于第一个while全部执行,第二个while实际只被执行一次就跳出来了,这种结构,很显然可以等价变换为外加一个大循环。那么在外部的大循环的作用下,第二个while循环可以“降维”, 去掉一层循环, 并根据pop()的现场来产生新的一批参数,给下一次循环使用。注意,两层循环合并后,循环终止条件也需要进行合并。

伪代码:
while (!(n == 0 && 栈空))  // 内外两层终止条件进行合并
    int i = n;
    while (i != 0) {
     push(i);
      i --;
    }
    int m = pop();
    move(m, ...);

    //产生新的参数
    n = m;
    n --;  // 对应hanoi(n - 1, pass, ...)
}

  最后进行完善,加上其他参数的变换。

 

源代码

#include <iostream>
#include <stack>
using namespace std;


void move(int n, char from, char to) {
        cout << "from " << from << " move " << n << " to " << to << endl;
}


struct param {
        int n;
        char from;
        char pass;
        char to;
};

void hanoi(int n, char from, char pass, char to) {
        stack<param> s;
        param par_outer = {n, from, pass, to};
        while (!(par_outer.n == 0 && s.empty())) {
                param par_inner = par_outer;
                while (par_inner.n > 0) {
                        s.push(par_inner);
                        par_inner.n --;
                        swap(par_inner.pass, par_inner.to);
                }

                par_outer = s.top();
                s.pop();

                move(par_outer.n, par_outer.from, par_outer.to);

                par_outer.n --;
                swap(par_outer.from, par_outer.pass);
        }
}


int main() {
        int N = 5;
        hanoi(N, 'A', 'B', 'C');

        return 0;
}

 

posted @ 2017-09-06 17:43  zcan  阅读(9910)  评论(0编辑  收藏  举报