龙空技术网

C语言解决《汉诺塔问题》!详细思路+源码分享

C语言编程 373

前言:

现在看官们对“c语言彩虹”大约比较看重,咱们都需要了解一些“c语言彩虹”的相关文章。那么小编同时在网上收集了一些有关“c语言彩虹””的相关内容,希望同学们能喜欢,我们一起来学习一下吧!

每天一个C语言小项目,提升你的编程能力!

汉诺塔问题源自印度一个古老的传说

印度教的“创造之神”梵天创造世界时做了 3 根金刚石柱,其中的一根柱子上按照从小到大的顺序摞着 64 个黄金圆盘。梵天命令一个叫婆罗门的门徒将所有的圆盘移动到另一个柱子上,移动过程中必须遵守以下规则:

(1)每次只能移动柱子最顶端的一个圆盘;

(2)每个柱子上,小圆盘永远要位于大圆盘之上。

在汉诺塔问题中,当圆盘个数不大于 3 时,多数人都可以轻松想到移动方案,随着圆盘数量的增多,汉诺塔问题会越来越难。也就是说,圆盘的个数直接决定了汉诺塔问题的难度,解决这样的问题可以尝试用分治算法,将移动多个圆盘的问题分解成多个移动少量圆盘的小问题,这些小问题很容易解决,从而可以找到整个问题的解决方案。

编译环境:Visual Studio 2019/2022,EasyX插件

这是经典问题汉诺塔的解题演示动画,代码如下:

#include <graphics.h>#include <conio.h>#include <stdio.h>#define MAX 64				// 圆盘的最大数目#define NULL 0// 定义栈struct STKNODE{	int a[4];};struct STK {	STKNODE*	stack[MAX];	int			top;};// 定义全局变量STK s[3];					// 声明三个栈,分别代表一号二号三号钢针上圆盘的状态int v = 5;					// 调整速度 // 函数声明void Initstk(STK* s);							// 初始化栈void Hannoi(int n, char a, char b, char c);		// 汉诺塔递归void start();									// 开始画面void Move(int n, char a, char b);				// 移动过程int switchab(char a);							// 返回钢针号void adjust();									// 调整速度暂停// 主函数void main(){	int n, ta[4] = {115, 500, 285, 518};			// 第一个圆盘的位置	printf("尽量小于16\n");							// 因为大于十六时就会显示有误,但程序可以正常运行	printf("请输入汉诺塔的层数(1~64):");	scanf("%d", &n);	STKNODE** p;	p = (STKNODE**)malloc(n * sizeof(STKNODE **));	// 声明一个元素为 n 个的动态 STKNODE 型指针数组	for (int i2 = 0; i2 < n; i2 ++)	{		p[i2] = (STKNODE *)malloc(sizeof(STKNODE));	// 为每一个指针申请空间	} 	Initstk(&s[0]);	Initstk(&s[1]);	Initstk(&s[2]);									// 将三个栈初始化	start();										// 呈现开始画面	setfillcolor(GREEN);							// 圆盘的颜色	for (int i=0; i < n; i++)	{		ta[0] += 5;		ta[1] -= 20;		ta[2] -= 5;		ta[3] -= 20;		solidrectangle(ta[0], ta[1], ta[2], ta[3]);			// 画出n个从大到小一次叠放的黄色圆盘		++s[0].top;									// 进栈		for (int i1 = 0; i1 < 4; i1++)		{			p[i]->a[i1] = ta[i1];			s[0].stack[s[0].top] = p[i];			// 记录每个矩形的位置,top为圆盘的个数		}	}		Hannoi(n, 'a', 'b', 'c');						// 汉诺塔递归函数	system("pause");	printf("\t\t\t\tGame Over!\n");}///////////////////////////////////////////////////// 函数定义// 汉诺塔的递归void Hannoi(int n, char a, char b, char c)  {	if(n == 1)		Move(1, a, c);	else	{		Hannoi(n-1, a, c, b);		Move(n, a, c);		Hannoi(n-1, b, a, c);    }}// 栈的初始化void Initstk(STK *s) {  	int i;	s->top = 0;	for (i = 0; i <= MAX; i++)		s->stack[++s->top] = NULL;	s->top = 0;}// 移动过程void Move(int n, char a, char b){	int i3, i4 = 0, i5 = 0;						i3 = b - a;												// 目的钢针与源钢针的位置差值	i4 = switchab(a);										// 源钢针钢针号	i5 = switchab(b);										// 目的钢针号	STKNODE *q1, *q0;										// 两个中间结点用于源栈和目的栈间的值得传递,q1为目的栈,q0为源栈	q1 = (STKNODE *)malloc(sizeof(STKNODE));	q0 = (STKNODE *)malloc(sizeof(STKNODE));	// 源栈与目的栈值的传递	q0 = s[i4].stack[s[i4].top];	++s[i5].top;											// 进栈	q1->a[0] = q0->a[0] + i3 * 200;	q1->a[1] = 500 - s[i5].top * 20;	q1->a[2] = q0->a[2] + i3 * 200;	q1->a[3] = 500 - s[i5].top * 20 + 18;	s[i5].stack[s[i5].top] = q1;	--s[i4].top;											// 出栈	// 向上运动	while (q0->a[1] >= 100)	{		setfillcolor(GREEN);		solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]);		adjust();											// 调整函数		Sleep(10 * v);										// 暂停(ms)		setfillcolor(WHITE);		solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]);		setlinecolor(RED);		line((q0->a[0] + q0->a[2]) / 2, q0->a[1], (q0->a[0] + q0->a[2]) / 2, q0->a[3]);	// 重新画上被擦掉原有的红线		q0->a[1] -= 10;		q0->a[3] -= 10;	}	// 向左或右运动,与 i3 的正负有关	while (q0->a[2] != q1->a[2])	{		setfillcolor(GREEN);		solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]);		adjust();		Sleep(10 * v);		setfillcolor(WHITE);		solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]);		if (i3 < 0)											// i3<0向左移		{			q0->a[0] -= 20;			q0->a[2] -= 20;		} 		else												// i3>0向右移		{			q0->a[0] += 20;			q0->a[2] += 20;		}	}	// 向下运动	while (q0->a[3] <= q1->a[3])	{		setfillcolor(GREEN);		solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]);		adjust();		Sleep(10 * v);		setfillcolor(WHITE);		solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]);		setlinecolor(RED);		if (q0->a[1] > 100)									// 重画被擦掉的红线		{			line((q0->a[0] + q0->a[2]) / 2, q0->a[1], (q0->a[0] + q0->a[2]) / 2, q0->a[3]);		}		q0->a[1] += 10;		q0->a[3] += 10;	}	// 在目的钢针上的相应位置绘制出黄色矩形块	setfillcolor(GREEN);	solidrectangle(q1->a[0], q1->a[1], q1->a[2], q1->a[3]);}// 绘制开始界面void start(){	// 初始化画面大小	initgraph(800, 650);	// 背景设为白色	setbkcolor(WHITE);	// 用白色填充整个画面	cleardevice();		// 绘制彩虹,形成一道彩虹,摘自 easyx 帮助文档示例程序	float H, S, L; 	H = 0;			// 色相	S = 1;			// 饱和度	L = 0.5f;		// 亮度	setlinestyle(PS_SOLID, 2);								// 设置线宽为 2	for(int r = 600; r > 544; r--)	{		H += 5;		setlinecolor( HSLtoRGB(H, S, L) );		circle(750, 900, r);	}	// 说明	settextstyle(50, 0, "华文楷体");	settextcolor(RED);	outtextxy(200, 150, "汉诺塔移动动画");	settextstyle(20, 0, "黑体");	outtextxy(600, 200, "BY:Ronald");	outtextxy(500, 200, "版本V1.1");	settextstyle(50, 0, "黑体");	settextcolor(GREEN);	outtextxy(200, 350, "随便按一个键开始吧!");	// 检测键盘敲击	getch();	// 清空开始界面	cleardevice();	// 绘制运动画面的的环境	setlinecolor(RED);	// 三根红色线段作为钢针	line(400, 110, 400, 500);	line(600, 110, 600, 500);	line(200, 110, 200, 500);	// 长方体形的底座	setfillcolor(LIGHTGRAY);	fillrectangle(80, 501, 720, 510); 		// 暂停按钮	solidrectangle(360, 540, 440, 580);	settextstyle(30, 0, "黑体");	settextcolor(GREEN);	outtextxy(370, 550, "暂停");                              	settextstyle(20, 0, "宋体");	settextcolor(RED);	outtextxy(300, 580, "鼠标暂停后请按空格继续");	// 加速按钮	solidrectangle(160, 540, 240, 580);	settextstyle(30, 0, "黑体");	settextcolor(GREEN);	outtextxy(170, 550, "加速");	settextstyle(20, 0, "宋体");	settextcolor(RED);	outtextxy(170, 580, "请按 d");	// 减速按钮	solidrectangle(560, 540, 640, 580);	settextstyle(30, 0, "黑体");	settextcolor(GREEN);	outtextxy(570, 550, "减速");	settextstyle(20, 0, "宋体");	settextcolor(RED);	outtextxy(570, 580, "请按 a");	// 说明	settextstyle(50, 0, "宋体");	settextcolor(GREEN);	outtextxy(10, 10, "正在进行中请欣赏:");}// 判断目的钢针与源钢针的钢针号返回钢针号int switchab(char a){	switch (a)	{		case 'a':			return 0;		case 'b':			return 1;		case 'c':			return 2;		default:			return 0;	}}// 调整函数,实现加速,减速,暂停void adjust(){	char f;												// 接收键盘敲进去的按钮和鼠标点击时赋予的变化值	// 用 f 接受键盘的键入值	if(kbhit())		f = getch();	// 检测鼠标消息	if (MouseHit()==true)										{		// 接收鼠标消息		MOUSEMSG Mouse;		Mouse = GetMouseMsg();		// 响应鼠标消息		if (Mouse.x >= 360 && Mouse.x <= 440 && Mouse.y >= 540 && Mouse.y <= 580 && Mouse.mkLButton)		{			f = ' ';		}		if (Mouse.x >= 160 && Mouse.x <= 240 && Mouse.y >= 540 && Mouse.y <= 580 && Mouse.mkLButton)		{			f = 'd';		}		if (Mouse.x >= 560 && Mouse.x <= 640 && Mouse.y >= 540 && Mouse.y <= 580 && Mouse.mkLButton)		{			f = 'a';		}	}	// 作用于动画	switch(f)	{				// 暂停		case ' ':					// 用‘继续’覆盖‘暂停’  			settextstyle(30, 0, "黑体");			settextcolor(GREEN);			outtextxy(370, 550, "继续");  			getch();					// 继续后变回显示‘暂停’			settextstyle(30, 0, "黑体");			settextcolor(GREEN);			outtextxy(370, 550, "暂停");  			break;				// 减速		case 'a':					// 当被点击时,‘减速’位置震动一下			setfillcolor(LIGHTGRAY);			solidrectangle(560, 540, 640, 580);			settextstyle(30, 0, "黑体");			settextcolor(GREEN);			outtextxy(575, 545, "减速"); 			Sleep(30);					// 减速			v++; 					// 回原位			setfillcolor(LIGHTGRAY);			solidrectangle(560, 540, 640, 580);			settextstyle(30, 0, "黑体");			settextcolor(GREEN);			outtextxy(570, 550, "减速");			break;				// 加速		case 'd':			setfillcolor(LIGHTGRAY);			solidrectangle(160, 540, 240, 580);			settextstyle(30, 0, "黑体");			settextcolor(GREEN);			outtextxy(165, 545, "加速");			Sleep(30);			setfillcolor(LIGHTGRAY);			solidrectangle(160, 540, 240, 580);			settextstyle(30, 0, "黑体");			settextcolor(GREEN);			outtextxy(170, 550, "加速");			// 加速			v--;					// v 最小为1			if (v <= 0)              			{				v = 1;			}			break;				default:			break;	} 		f = 'r';									// f 初始化为 r	FlushMouseMsgBuffer();						// 清空鼠标消息}

大家赶紧去动手试试吧!

此外,我也给大家分享我收集的其他资源,从最零基础开始的教程到C语言C++项目案例,帮助大家在学习C语言的道路上披荆斩棘!

编程学习书籍分享:

编程学习视频分享:

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!

标签: #c语言彩虹