汉诺塔问题的递归程序很简单,也很容易理解,C源程序如下。

// 程序1
#include <stdio.h>
int n;
void move(int k, char from, char to) {
  static int Ser_num;
  Ser_num++;
  printf("%3d  k=%2d   %c-->%c\n",Ser_num,k,from, to);
  return;
}
void hanoi(int k,char from,char pass,char to) {
  if(k==0) return;
  else {
    hanoi(k-1,from,to,pass);
    move(n-k+1,from,to);
    hanoi(k-1,pass,from,to);
  }
}
int main() {
  printf("请输入A座上的盘子数目(0<=exit):");
  scanf("%d",&n);
  if (n>0){
    printf("序号 盘号   移动\n");
    hanoi(n,'A','B','C');
  }
  return 0;
}

汉诺塔问题的非递归程序实现要复杂一些,在上一篇文章中介绍了一种借助于堆栈,解决该问题的解法。下面根据文献1描述的方法介绍一种有趣的汉诺塔问题非递归方法。

文献1利用满二叉树的性质提出了汉诺塔问题的非递归算法,为便于叙述,假设AC为满二叉树的一个结点,它表示金盘从A柱移到C柱,同理BC结点表示金盘从B柱移到C柱,其他类推。

那么,你发现汉诺塔与满二叉树有什么关系呢?下面简要介绍。

性质1:n个盘子的汉诺塔问题需要移动盘子的总数为2n-1,而深度为n的满二叉树正好有2n-1个结点,汉诺塔的每一个盘子的移动都对应满二叉树中的一个结点。很奇妙吧!

当n=1时,汉诺塔中的盘子仅需要移动1次,即1A->C(注:此处的数字指第几层的盘子,后面相同),对应于仅有一个根结点AC的满二叉树,如图1所示n=1的情况。

当n=2时,移动3次,分别是2A->B,1A->C,2B->C,对应满二叉树中的结点AB,AC,BC,如图1所示n=2的情况。

当n=3时,移动7次,如图1所示n=3的满二叉树。

python非递归解决八皇后 非递归程序_算法

图1 满二叉树与汉诺塔的关系

性质2:n=2的满二叉树是由n=1的满二叉树派生的,n=3的满二叉树是由n=2的满二叉树派生的,其他类推。例如,结点AB派生的左右子树分别是AC和CB。即设P={A,B,C},若结点为sd,其中s∈P,d∈P,那么,结点sd派生出的左右子树的根结点分别是sp和pd,其中p∈P-{s, d},例如AB派生出的两个子结点分别是AC和CB。

性质3:盘子的移动顺序是满二叉树的中序遍历。例如深度为3的满二叉树的中序遍历为:AC、AB、CB、AC、BA、BC、AC。正好对应3A->C,2A->B,3C->B,1A->C,3B->A,2B->C,1A->C。

除了上面3个性质外是否还有其他性质呢?我们继续寻找。

下面给出完成的源程序。

#include <stdio.h>
int n;
void move(int k, char from, char to) {
  static int Ser_num;
  Ser_num++;
  printf("%3d  k=%2d   %c-->%c\n",Ser_num,k,from, to);
  return;
}
void hanoi(int n) {
  int movenum=1;
  int i,m,n_k,k,j;
  for (i=0; i<n; i++) {
    movenum *= 2;
  }
  movenum--; // 计算移动盘次数
  for (i=1; i<=movenum; i++) {
    m=i;
    n_k=0;
    while (m%2==0) {
      n_k++;
      m=m/2;
    }
    k=n-n_k;
    j=m/2;
    if (k%2==0) {
      switch(j%3) {
        case 0: move(k,'A','B'); break;
        case 1: move(k,'B','C'); break;
        case 2: move(k,'C','A'); break;
      }
    } else {
      switch(j%3) {
        case 0: move(k,'A','C'); break;
        case 1: move(k,'C','B'); break;
        case 2: move(k,'B','A'); break;
      }
    }
  }
}
int main() {
  printf("请输入A座上的盘子数目(0<=exit):");
  scanf("%d",&n);
  if (n>0) {
    printf("序号 盘号   移动\n");
    hanoi(n);
  }
  return 0;
}

参考文献:

[1] 谭罗生,吴福英,黄明和. Hanoi塔问题的解模型[J]. 计算机应用与软件,2004,21(10):49-51.