7-17 汉诺塔的非递归实现 (25分)

一开始看见通过了0.4+,以为是送分题,结果我错了。

花了好长时间看博客没搞懂怎么非递归实现(菜……)。

后面看了

https://blog.csdn.net/computerme/article/details/18080511算法https://zhuanlan.zhihu.com/p/36085324非递归实现方法受到启发才把代码给敲出来。

下面简单说一下我理解到的方法吧!

第一步是判断输入的n是奇数还是偶数,若为奇数,则按顺时针以ACB的顺序摆成品字型,若为偶数,则按顺时针以ABC的顺序摆成品字型。(参考下图)

第二步将序号为1(最小)的盘,按顺时针放到下一个字母。假如以ABC顺序顺时针摆放时,若1盘在A,则将它移动到B,若在B则移动到C……

第三步处理剩余两个字母(1盘此时不在的那两个字母),暂且称为X和Y。可能会出现如下几种情况:

1)X和Y都有盘,此时他们顶层的盘必能比较出大小,那么这时候将小的盘移动到大的盘上面,结束。

2)X和Y都没盘,不用做任何处理。(比如输入的n为1的时候就会出现这种情况)

3)X和Y一个有盘,一个没盘,这时候将有盘的一边的盘移动到没有盘的一边,结束。

只要我们循环运行第二步和第三步就能完成汉诺塔的非递归实现啦。

下面是我的AC代码:

  1 //非递归AC代码
  2 //用cout最后一个测试会超时,改为printf就AC了
  3
  4 #include <iostream>
  5 #include <string>
  6 #include <cstring>
  7 #include <stdio.h>
  8 #include <cmath>
  9 using namespace std;
 10 int n;//输入的盘子数
 11
 12 class stack_//“三根柱子”的类型
 13 {
 14 public:
 15     stack_() :r(0){}
 16     ~stack_() {}
 17     void push(int k)
 18     {
 19         h[++r] = k;
 20     }
 21     int pop()
 22     {
 23         if (r <= 0)
 24             return 0;
 25         else
 26         return h[r--];
 27     }
 28 public:
 29     char name;
 30     int r;//指针
 31     int h[10000];
 32 };
 33 stack_ S[3];//将三根柱子定义为数组,方便操作
 34
 35 bool is_over(int cnt)//判断移动次数有没有超过2^n-1
 36 {
 37     if (cnt >= pow(2, n)-1)
 38         return true;
 39     else
 40         return false;
 41 }
 42
 43 void move(char a, char b, char c)//移动函数主体
 44 {
 45     int pop_x,temp1,temp2;
 46     int cnt = 0;//累计移动的次数
 47     int i = 0;//循环的次数
 48
 49     while(cnt < pow(2,n)-1 )//移动次数到达最大时就要退出循环,继续循环会导致错误
 50     {
 51         int k;//中间变量,简化式子
 52         pop_x = S[i % 3].pop();//随着i的变化,可以实现对S[0],S[1],S[2]轮回判断
 53         if (pop_x == 1)
 54         {
 55             k = i + 1;
 56             S[k % 3].push(1);
 57             if (!is_over(cnt))
 58             {
 59                  //cout << S[i % 3].name << " -> " << S[k% 3].name << endl;
 60                 printf("%c -> %c\n", S[i % 3].name, S[k % 3].name);
 61                     cnt++;
 62             }
 63             temp1 = S[(k + 1) % 3].pop();
 64             temp2 = S[(k - 1) % 3].pop();
 65             if ((temp1 != 0 && temp2 != 0)&& (temp1 < temp2)  || temp2 == 0 and temp1 != 0)//temp1 移动到 temp2 的情况
 66             {
 67                 S[(k - 1) % 3].push(temp2);
 68                 S[(k - 1) % 3].push(temp1);
 69                 if (!is_over(cnt))
 70                 {
 71                     //cout << S[(k + 1) % 3].name << " -> " << S[(k - 1) % 3].name <<endl;
 72                     printf("%c -> %c\n", S[(k+1) % 3].name, S[(k-1) % 3].name);
 73                     cnt++;
 74                 }
 75
 76             }
 77             else if (temp1 == 0 && temp2 == 0)
 78             {
 79                 //不移动任何盘子,只要把刚刚出栈的元素重新压回去
 80                 S[(k + 1) % 3].push(temp1);
 81                 S[(k - 1) % 3].push(temp2);
 82             }
 83             else
 84             {
 85                 S[(k + 1) % 3].push(temp1);
 86                 S[(k + 1) % 3].push(temp2);
 87                 if (!is_over(cnt))
 88                 {
 89                     //cout << S[(k - 1) % 3].name << " -> " << S[(k + 1) % 3].name << endl;
 90                     printf("%c -> %c\n", S[(k-1)% 3].name, S[(k+1) % 3].name);
 91                     cnt++;
 92                 }
 93             }
 94             i++;//注意在末尾将i的值加1,实现0,1,2的轮回
 95         }
 96         else
 97         {
 98             S[i % 3].push(pop_x);//不符合条件,重新压回栈
 99             i++;
100         }
101     }
102     //cout << endl << cnt << endl;
103 }
104
105
106 void hanoi(int n, char a, char b, char c)//接口
107 {
108     S[0].name = a,
109     S[1].name = b;
110     S[2].name = c;
111     for (int i = n; i >= 1; i--)//从大到小将盘子压入栈
112     {
113         S[0].push(i);
114     }
115        move(a, b, c);//调用move开始进行移动
116 }
117
118 int main()
119 {
120    cin >> n;
121     if (n % 2 == 0)
122         hanoi(n, ‘a‘, ‘b‘, ‘c‘);//偶数的时候按abc顺序
123     else
124         hanoi(n, ‘a‘, ‘c‘, ‘b‘);//奇数的时候按acb顺序
125     return 0 ;
126 }

再附加个递归实现的:

 1 //递归实现
 2 #include <iostream>
 3 #include <string>
 4 #include <cstring>
 5
 6 using namespace std;
 7 void hanoi(int n, char a, char b, char c)
 8 {
 9     if(n==1)
10     {
11         cout << a <<" -> " << c << endl;
12         return;
13     }
14     else
15     {
16         hanoi(n-1,a,c,b);
17         cout << a <<" -> " << c <<endl;
18         hanoi(n-1,b,a,c);
19         return;
20     }
21 }
22
23 int main()
24 {
25     int n ;
26     cin >> n;
27     hanoi(n,‘a‘,‘b‘,‘c‘);
28     return 0;
29 }

对于递归算法我的个人理解:
首先将初始盘子看成是n-1的整体和最下面的最大一块的组合体。

hanoi(n-1,a,c,b);
cout << a <<" -> " << c <<endl;
hanoi(n-1,b,a,c);

hanoi(n,A,B,C,)中A为初始位置,C为目标位置,上述代码第一步目的是将n-1整体从A移动到B,所以调用hanoi(n-1,A,C,B)。依此类推,第二步是要把最大一块从A移动到C,所以也可写成hanoi(1,A,B,C),第三步同理。

那么为什么hanoi(n-1,A,B,C)可以实现将上面的n-1个盘从A移动到B呢?

我的理解:

当n=2时,即n-1为1的时候,这个函数显然是可以实现这一目标的。当n=3时n-1就为2了,这个时候调用hanoi(n-1,A,B,C)其实就是调用hanoi(2,A,B,C),那么我们刚刚已经确定参数为2的时候是可以达到目的的,那么可以推出,n为3的时候也可以达到目的(因为他是借助n-1=2时的函数实现的),于是就可以继续往后面推断出n为任何数字的时候都可以实现这一功能。

原文地址:https://www.cnblogs.com/2020R/p/12388566.html

时间: 2024-10-07 06:54:25

7-17 汉诺塔的非递归实现 (25分)的相关文章

5-17 汉诺塔的非递归实现 (25分)

5-17 汉诺塔的非递归实现   (25分) 借助堆栈以非递归(循环)方式求解汉诺塔的问题(n, a, b, c),即将N个盘子从起始柱(标记为"a")通过借助柱(标记为"b")移动到目标柱(标记为"c"),并保证每个移动符合汉诺塔问题的要求. 输入格式: 输入为一个正整数N,即起始柱上的盘数. 输出格式: 每个操作(移动)占一行,按柱1 -> 柱2的格式输出. 输入样例: 3 输出样例: a -> c a -> b c -&g

7-17 汉诺塔的非递归实现

7-17 汉诺塔的非递归实现(25 分) 借助堆栈以非递归(循环)方式求解汉诺塔的问题(n, a, b, c),即将N个盘子从起始柱(标记为"a")通过借助柱(标记为"b")移动到目标柱(标记为"c"),并保证每个移动符合汉诺塔问题的要求. 输入格式: 输入为一个正整数N,即起始柱上的盘数. 输出格式: 每个操作(移动)占一行,按柱1 -> 柱2的格式输出. 输入样例: 3 输出样例: a -> c a -> b c ->

汉诺塔的非递归实现(栈)

汉诺塔的非递归实现(栈) 美国学者找的规律:若是偶数,将a.b.c顺时针排列,否则a.c.b排列,然后反复做: (1)最小盘顺时针移动一个 (2)那两个柱子将最小的移动了,空的话直接移 借助堆栈以非递归(循环)方式求解汉诺塔的问题(n, a, b, c),即将N个盘子从起始柱(标记为"a")通过借助柱(标记为"b")移动到目标柱(标记为"c"),并保证每个移动符合汉诺塔问题的要求. 输入格式: 输入为一个正整数N,即起始柱上的盘数. 输出格式:

汉诺塔问题的递归解法

汉诺塔问题的递归解法: 实现程序: #include<iostream> using namespace std; void move(int n, char i, char j) { cout << "把" << n << "号从" << i << "移动到" << j << endl; } void hanoi(int n, char x, cha

汉诺塔问题(递归与非递归)

汉诺塔比较经典的实现是利用递归,但也可以利用堆栈. 题意理解:有A,B,C三个柱子,将A柱子上的N个盘子(从大到小排列)移到C柱子上,每次只允许移动一个盘子,并且保证每个柱子上的盘子的排列都是从大到小. 1.递归实现 假设只有一个盘子,那么只需实现 A->C 这个动作: 如果有两个盘子,那么需要 (1)A->B; (2)A->C; (3)B->C; 如果有三个盘子,可以将前两个盘子看作一个盘子,对两个盘子重复以上三个步骤,于是得到N个盘子的递归算法,递归结束的条件是N=1: 1 v

汉诺塔问题非递归算法集锦

巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 汉诺塔问题介绍: 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔.不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片,一次只移动一片,不管在哪根针上,小片必在大片上面.当所有的金片都从梵天穿好的那根针上移到另外一概针上时,世界就将在一声霹

汉诺塔问题的递归实现

汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上.并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘. 1.汉诺塔(基本) 汉诺塔问题是典型的分治算法问题,首先我们来讨论最基本的汉诺塔问题.假设有n个圆盘,三根柱子,a,b,c,需要把n个盘子(从上往下从小到大摞着)从a柱移动到c柱,在小圆盘上不能放大圆盘,在三根柱

汉诺塔的简单递归思想

最近在校招打酱油,闲得没事想起了大一学过的汉诺塔,不过那时只知道玩游戏,一次性3~7个玩过了,还是有成就感的呵呵.游戏链接:http://www.7k7k.com/swf/335.htm. 看了网上很多递归方法,像 1 #include<stdio.h> 2 3 void move(int n,char a,char b,char c) 4 { 5 if(n==1) 6 printf("\t%c->%c\n",a,c); 7 else 8 { 9 move(n-1,a

汉诺塔问题(递归、栈)

修改一下汉诺塔的游戏规则,现在不能直接从左边走到右边,也不能直接右边走到左边. 方法一:递归实现 现在分析一下,比如左边有1~n,那么移动最后一个的情况,就是: 1.1-n-1从左边移动到右边 2.n从左边移动到中间 3.1-n-1从右边移动到左边 4.n从中间移动到右边 5.1-n-1从左边移动到右边 那么,假如我有这样一个f(range,from,to)那么我需要求解的就是f(n,lrft,right),原子情况就是从只有一个数的时候,直接左边中间右边即可. 1 public static