递归3: 汉诺塔的递归与迭代实现

递归实现与main():

/*------------------------------------------------------
汉诺塔主要是有三个塔座X,Y,Z,要求将从小到大编号为 1,2.....n 的
圆盘从X移动到塔座Z上,要求
 (1):每次只能移动一个圆盘
 (2):圆盘可以插到X,Y,Z中任一塔座上
 (3):任何时候不能将一个较大的圆盘压在较小的圆盘之上
 初始:所有圆盘都在 X 塔座,并且最大的圆盘在最底部,然后是次大的;
 结束:所有圆盘都在 Z 塔座,并且最大的圆盘在最底部,然后是次大的;
------------------------------------------------------*/
#include <iostream>
#include <ctime>	// time()
using namespace std;
/*-----------------------------------------------------------
前置条件: n > 0
后置条件: 输出将n个圆盘从源塔座(orig)移动到目的塔座(dest)的步骤,
		 临时塔座(temp)用于临时存放。
算法(递归):
		n == 1时,把盘1从源移动到目的地
		n > 1时,
			1)将n-1个圆盘(每次移动一个)从源移动到临时塔座
			2)将盘n从源移动到目的地
			3)将n-1个圆盘(每次移动一个)从临时塔座移动到目的塔座
时间复杂度: wotstTime(n) 是 O(2^n).
空间复杂度: worstSpace(n) 是 O(n).
-----------------------------------------------------------*/
void move(int n, char orig[], char dest[], char temp[])
{
	static char ori = 'X', tem = 'Y', des = 'Z', ch;	// 三个塔座
	static int num = 0, length = n;		// num记录移动步数, length保存圆盘数目
	if (n <= 0){	// 处理参数传递错误
		cout << "The value is error!\n";
		return;
	}
	if (n == 1){	// 通用策略: n == 1时情况
		dest[n-1] = orig[n-1];
		num++;
		cout << num << ") Disc " << orig[n-1] << ": "
			 << ori << "-->" << des << endl;
	}
	else{			// 通用策略: n > 1时情况
		ch = des; des = tem; tem = ch;
		move(n-1, orig, temp, dest);
		ch = des; des = tem; tem = ch;

		dest[n-1] = orig[n-1];
		num++;
		cout << num << ") Disc " << orig[n-1] << ": "
			 << ori << "-->" << des << endl;

		ch = ori; ori = tem; tem = ch;
		move(n-1, temp, dest, orig);
		ch = ori; ori = tem; tem = ch;
	}
	if (dest[length] != '\0')
		dest[length] = '\0';
}
// move中声明的局部变量为静态的,这样在使用递归调用的move函数里只在第一次定义时初始化;
// 塔座名ori、tem、des要随着递归调用时参数的传递而变化,递归调用结束后应该及时恢复;
// n为圆盘数目,塔座结构采用C-styel字符串,因注意字符串末尾要有'\0';

void movecopy(int, char [], char [], char []);
int main()
{
	long start_time, finish_time, elapsed_time1, elapsed_time2;
	const int N = 5;		// 圆盘数目为 N-1
	char orig[N] = "abcd", temp[N], dest[N];

	// 计算move()函数运行了多少时间(time()的返回值类型为long)
	cout << "move::orig = " << orig << endl;
	start_time = time(NULL);
	move(N-1, orig, dest, temp);
	finish_time = time(NULL);
	elapsed_time1 = finish_time - start_time;
	cout << "move::dest = " << dest << endl << endl;

	// 计算movecopy()函数运行了多少时间
	cout << "movecopy::orig = " << orig << endl;
	start_time = time(NULL);
	movecopy(N-1, orig, dest, temp);
	finish_time = time(NULL);
	elapsed_time2 = finish_time - start_time;
	cout << "movecopy::dest = " << dest << endl << endl;

	// move()和movecopy()运行时间比较
	cout << "move::The elapsed time was " << elapsed_time1
		 << " senonds." << endl;
	cout << "movecopy::The elapsed time was " << elapsed_time2
		 << " senonds." << endl;
	return 0;
}

迭代实现: 

/*--------------------------------------------------------------
前置条件: n > 0
后置条件: 输出将n个圆盘从源塔座(orig)移动到目的塔座(dest)的步骤,
		 临时塔座(temp)用于临时存放。
算法(迭代): 
		1)确定哪一个圆盘要移动。
			a.移动次数: c(n) = 2^n -1;
			b.m为n位的二进制数,则m的取值范围为0~2^n -1。
			规律:让m每次递增1,可以发现,m中最高一位的刚刚由0变为1的位置的位置编号,
				 和即将要移动的盘子编号有确定关系。 
		2)这个盘子往哪个塔座上移动。 
			a.n为奇数时,奇数编号圆盘按顺时针移动(X->Y->Z->X),偶数编号圆盘按逆时针
			  移动(X->Z->Y->X);
			b.n为偶数时,偶数编号圆盘按顺时针移动(X->Y->Z->X),奇数编号圆盘按逆时针
			  移动(X->Z->Y->X);
--------------------------------------------------------------*/
void movecopy(int n, char orig[], char dest[], char temp[])
{
	char ori[] = "orig(X)", tem[] = "temp(Y)", des[] = "dest(Z)";// 三个塔座
	int num = 0;		// num记录移动步数
	int m1[32], m2[32], tempnum, i, j, k;
	// m1、m2数组存储 n的二进制bit位,且 n(m2对应) = n(m1对应) + 1;
	// i、j分别为m1、m2中存储n值要求的二进制的最小bit数;
	// k+1 为求得的盘子编号
	int x = n, y = 0, z = 0;
	// x、y、z分别跟踪三个塔座当前的圆盘数目
	if (n == 0){		// 处理参数传递错误
		cout << "The value is error!\n";
		return;
	}

	// 保存orig中元素到ch[32],然后反转orig塔座中圆盘的顺序
	// (如:'a'在orig中第一个位置,但是 'a' 也在塔座的最上方--第n-1个位置)
	char ch[32];
	for (i = 0; i < n; i++)
		ch[i] = orig[i];
	for (int length = n-1, i = 0; i < n; i++, length--){
		orig[i] = ch[length];
	}
	ch[n] = '\0';
	dest[n] = '\0';
	temp[n] = '\0';

	for (int l = 0; l < (1<<n)-1; l++)	// 移动次数:2^n-1 既是 (1<<n)-1
	{
		// 1)确定哪一个圆盘要移动(求 k+1 的值)
		tempnum = l;
		for (i = 0; tempnum != 1 && tempnum != 0; i++)
			{m1[i] = tempnum%2; tempnum = tempnum/2;}
		m1[i] = tempnum;
		tempnum = l+1;
		for (j = 0; tempnum != 1 && tempnum != 0; j++)
			{m2[j] = tempnum%2; tempnum = tempnum/2;}
		m2[j] = tempnum;
		if (j > i) k = j;
		else
			for (k = j; m2[k] == m1[k]; k--) ;

		num++;
		cout << num << ") Disc " << ch[k] << ": ";

		// 2)这个盘子往哪个塔座上移动。
		if (((n % 2 == 0 && ((k+1)%2 == 0))) ||	// (n为偶数,偶数编号圆盘顺时针移动)
			((n % 2 != 0) && ((k+1)%2 != 0))){	// (n为奇数,奇数编号圆盘顺时针移动)
				for (i = 0; ch[k] != orig[i] && i < x; i++) ;
				if ((i > 0 && i < x) || (i == 0 && orig[i] == ch[k])){
					dest[y] = orig[i];
					y++; x--; cout << ori << "-->" << des;
					orig[i] = '\0';
				}
				else{	// (如果在orig塔座没有找到需要圆盘)
					for (i = 0; ch[k] != dest[i] && i < y; i++) ;
					if ((i > 0 && i < y) || (i == 0 && dest[i] == ch[k])){
						temp[z] = dest[i];
						z++; y--; cout << des << "-->" << tem;
						dest[i] = '\0';
					}
					else{	// (如果在orig和dest塔座都没有找到需要圆盘)
						for (i = 0; ch[k] != temp[i] && i < z; i++) ;
						if ((i > 0 && i < z) || (i == 0 && temp[i] == ch[k])){
							orig[x] = temp[i];
							x++; z--; cout << tem << "-->" << ori;
							temp[i] = '\0';
						}
					}
				}
		}
		else{	// (n为奇数,偶数编号圆盘逆时针移动)(n为偶数,奇数编号圆盘逆时针移动)
			for (i = 0; ch[k] != orig[i] && i < x; i++) ;
			if ((i > 0 && i < x) || (i == 0 && orig[i] == ch[k])){
				temp[z] = orig[i];
				z++; x--; cout << ori << "-->" << tem;
				orig[i] = '\0';
			}
			else{	// (如果在orig塔座没有找到需要圆盘)
				for (i = 0; ch[k] != dest[i] && i < y; i++) ;
				if ((i > 0 && i < y) || (i == 0 && dest[i] == ch[k])){
					orig[x] = dest[i];
					x++; y--; cout << des << "-->" << ori;
					dest[i] = '\0';
				}
				else{	// (如果在orig和dest塔座都没有找到需要圆盘)
					for (i = 0; ch[k] != temp[i] && i < z; i++) ;
					if ((i > 0 && i < z) || (i == 0 && temp[i] == ch[k])){
						dest[y] = temp[i];
						y++; z--; cout << tem << "-->" << des;
						temp[i] = '\0';
					}
				}
			}
		}
		cout << endl;
	}

	// 保存dest中元素到ch[32],然后反转dest塔座中圆盘的顺序
	for (i = 0; i < n; i++)
		ch[i] = dest[i];
	for (int length = n-1, i = 0; i < n; i++, length--){
		dest[i] = ch[length];
	}
}

运行结果:

《递归3: 汉诺塔的递归与迭代实现》

我把main()中变动为 const int N = 15; 时,move()运行了 44 seconds,而movecopy()运行了 80 seconds..

这个递归版本明显好于迭代版本

    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/yss28/article/details/12850453
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞