汉诺塔实现的基本思路是:不断将n个盘的汉诺塔问题转换为2个n - 1个盘的汉诺塔问题,一次用递归实现是很自然的方法。当吧n盘问题转换为n -1 个盘的问题时, 问题的起始柱子和目标柱子也发生了变化,设n盘问题为(n, a, b, c),其中参数如下结构体所定义,则问题求解可转换为对(n - 1, a, c, b)、(1, a, b, c)、(n - 1, b, a, c)这三个问题的求解,其中(1, a, b, c)不需要递归,可直接实现。
但是若是用堆栈来实现的话,当将分解出的上述三个问题雅茹堆栈时,应该按照“需要先求解的问题后压入”的顺序,也就是压入顺序为:(n - 1, b, a, c), (1, a, b, c), (n - 1, a, c, b).
1 typedef struct { //汉诺塔问题的结构类型 2 int N; 3 char A; //起始柱 4 char B; //借助柱 5 char C; //目标柱 6 7 }ElementType; //汉诺塔问题的结构类型
1 //借助栈的非递归实现 2 void Hanoi(int n) 3 { 4 ElementType P, toPush; 5 Stack S; 6 7 P.N = n; P.A = 'a'; P.B = 'b'; P.C = 'c'; 8 S.top = -1; 9 10 Push(&S, P); 11 while (S.top != -1) //当堆栈不为空时 12 { 13 P = Pop(&S); 14 if (P.N == 1) 15 printf("%c -> %c\n", P.A, P.C); 16 else 17 { 18 toPush.N = P.N - 1; 19 toPush.A = P.B; toPush.B = P.A; toPush.C = P.C; 20 Push(&S, toPush); //将第二个待解子问题(n - 1, b, a, c)入栈 21 toPush.N = 1; 22 toPush.A = P.A; toPush.B = P.B; toPush.C = P.C; 23 Push(&S, toPush); //将可直接求解的子问题(1, a, b, c)入栈 24 toPush.N = P.N - 1; 25 toPush.A = P.A; toPush.B = P.C; toPush.C = P.B; 26 Push(&S, toPush); //将第一个待解子问题(n - 1, a, c, b)入栈 27 } 28 } 29 }
下面是栈的实现和主函数:
1 #include <stdio.h> 2 #define MaxSize 100 3 4 typedef struct { 5 ElementType Data[MaxSize]; 6 int top; 7 }Stack; //堆栈的标准定义 8 9 void Push(Stack *PtrS, ElementType item) 10 { 11 //入栈操作 12 if (PtrS->top == MaxSize) 13 { 14 printf("The stack is full!\n"); 15 return; 16 } 17 else 18 { 19 PtrS->Data[++(PtrS->top)] = item; 20 return; 21 } 22 } 23 24 ElementType Pop(Stack *PtrS) 25 { 26 if (PtrS->top == -1) 27 { 28 printf("The stack is empty!\n"); 29 return ERROR; //ERROR是ElementType的特殊值,标志错误 30 } 31 else 32 { 33 PtrS->top--; 34 return (PtrS->Data[PtrS->top + 1]); //或者是return PtrS->Data[PtrS->top--]; 35 } 36 } 37 38 int main() 39 { 40 int n; 41 ERROR.N = -1; //ERROR是ElementType的特殊值,标志错误 42 scanf_s("%d", &n); 43 Hanoi(n); 44 return 0; 45 }