【数据结构】课程设计:马踏棋盘

该课程设计利用栈实现马踏棋盘问题的非递归算法,通过while循环控制遍历,并借助QT创建应用界面进行算法的可视化展示。用户输入马的起始位置,程序计算出遍历路径,将数字1-64填入棋盘。在实现过程中,解决了遍历死循环和程序崩溃的问题,优化了遍历规则以减少回溯,提高了效率。
摘要由CSDN通过智能技术生成

本课程设计使用了栈来记录马行进的路线,使用while循环控制马遍历的次数以实现对棋盘的非递归遍历,并利用了QT设计应用程序界面,实现了算法的可视化。

工程代码(内附程序安装包)

Repo: Keikei99/ChessBoardProblem: My Data Structure curriculum assignment (github.com)

设计任务及要求

任务:设计一个国际象棋的马踏遍棋盘的演示程序。

要求:将马随机放在国际象棋的8×8棋盘Board的某个方格中,马按走棋规则进行移动。要求每个方格只进入一次,走遍棋盘上全部64个方格。编制非递归程序,求出马的行走路线,并按求出的行走路线,将数字1,2,…,64依次填入一个8×8的方阵,并输出。

设计主体

需求分析

用户输入马在棋盘中的起始位置,程序需要不断遍历寻找棋子的落点,最终在棋盘上用1至64的标号来呈现马遍历棋盘的过程。

程序流程图和函数流程图如图1和图2所示。

在这里插入图片描述

在这里插入图片描述

本程序以马的起始位置坐标(i, j)作为输入,其中1<=i, j<=8,同时以带有标号的棋盘作为程序的输出。程序能够根据给定的起始位置坐标进行循环遍历,找到马完全遍历棋盘的路线。若输入输入的起始坐标满足条件1<=i, j<=8,则输入正确,需要输出马遍历棋盘的完整路线,否则输入错误,需要输出对应的指示引导使用者重新输入。

系统设计

马的走棋规则

如图3所示,当马的起始位置(i, j)确定的时候,可以走到下列8个位置之一:

(i-2,j+1)、(i-1,j+2)、(i+1,j+2)、(i+2,j+1)、(i+2,j-1)、(i+1,j-2)、(i-1,j-2)、(i-2,j-1)

在这里插入图片描述

因此规定移动矩阵Move如下表1所示。

Move01234567
MoveX-2-11221-1-2
MoveY1221-1-2-2-1

数据结构定义

本课程设计使用到的数据结构定义如下:

在这里插入图片描述

模块说明:

在这里插入图片描述

函数调用关系图:

在这里插入图片描述

主程序伪代码:

在这里插入图片描述

主要模块伪代码:

在这里插入图片描述

系统实现

算法实现

  • 第一步:用户输入马的起始坐标(i, j),其中1<=i, j<=8。

  • 第二步:初始化栈Stack,将起始点进栈;

  • 第三步:利用移动矩阵Move顺时针搜索P0附近的8个方向,并计算每个方向的下一个位置对应的权值。此处定义权值为该点可行方向的数量;定义点的index值为0;

  • 第四步:将得到的权值存入direction数组中,并判断该数组是否为空;

  • 第五步:若direction数组不为空,则对direction数组进行快速升序排序,根据index的值,优先访问权值最小的可行方向,将可行点进栈,计数器order自增1;若direction数组为空,说明该点已无可行方向,此时需要进行退栈操作,计数器order自减1,direction数组的指针index自增1,并对刚刚退出栈顶的点重新进行搜索。

  • 重复第三、四、五步,直到order>64时循环结束。

  • 第六步:利用双重for循环遍历输出Board二维数组。

程序调试中遇到的问题

  1. 遍历过程进入死循环

在改进算法前,算法在8个方向的搜索规则是:若存在可行的方向,则前进,否则后退。然而,当该点有且只有一个可行方向,且下一个方向对应的点无可行方向时,马会前进到下一个点,而因无可行方向回退,重新搜索时又会前进到这个“死胡同”。

因此,考虑到要提升程序运行的效率,就需要重新制定遍历规则,减少棋子回溯的次数,尽量使其能够一次性遍历整个棋盘。新的规则即是计算该点8个可行方向下的下一个点的权值,并优先访问权值较小的方向,进而减少回溯的次数,提升算法的运行效率。

  1. 算法可视化时程序崩溃

在实现算法以后,考虑将本算法移植到QT工程中实现可视化的应用程序。在移植的过程中,我设计了程序的ui界面,使用connect函数连接信号与槽,同时创建了一个ChessBoardProblem类用于算法运行。然而在点击“计算结果”按键时,程序无响应。

后来通过debug调试过程发现程序因使用了未定义的变量而产生异常,原因是我将原程序的全局变量定义为了ChessBoardProblem类的私有成员变量,导致函数之间的调用时存在变量值未更改的问题。我将私有成员变量设置为静态私有成员变量后,程序不再崩溃。

对设计与实现的回顾

本题要求使用非递归算法实现马踏棋盘的遍历计算,因此在算法中使用while循环来控制棋子的遍历次数。当order大于64时说明棋盘已遍历完成,此时需要退出while循环。

同时,算法使用栈这一基本数据结构实现“回溯”功能。当马向下一个点前进时,将下一个点的信息进行入栈操作;而当马后退时,需要弹出栈顶元素并进行重新搜索。最终实现了马的“回溯”功能。

算法的时空分析和改进思想

在8×8的棋盘Board中,根据现有的搜索规则,程序对给定的初始点绝大部分可以在无需回溯的条件下遍历完整个棋盘。只有当初始点为(4, 2)时,程序发生回溯。

算法能够通过进一步优化遍历规则,减少无效的搜索次数和棋子回溯的次数,尽量使其能够一次性遍历整个棋盘。

QT Creator的分文件结构

在这里插入图片描述

用户手册

如图6所示,在本次数据结构课程设计中使用QT Creator软件制作了算法的可视化界面。通过选定初始点的横坐标和纵坐标,即可对算法进行遍历计算,进而呈现马遍历棋盘的完整路线。

在这里插入图片描述

如图7所示,本次以初始点(5, 2)为例子,选定初始横坐标为5,初始纵坐标为2,点击“计算结果”按钮,对棋子的运行路线进行遍历计算。

在这里插入图片描述

上图清晰呈现了马从(5, 2)点遍历全棋盘的次序,此时点击“动画演示”按键可以逐步呈现马的行走路线。如图所示,棋盘中红色格子代表马所在的位置,绿色格子代表马已经走过的位置。

在这里插入图片描述

在这里插入图片描述

演示完毕。若需要重新演示,可以点击棋盘旁的“重新开始”按钮或点击“开始菜单”中的“重新开始”按钮重新选择测试用例。

在这里插入图片描述

在这里插入图片描述

点击“开始菜单”的帮助文档按钮可以查询马踏棋盘中马的行走规则和程序的相关使用方法。

在这里插入图片描述

参考资料

[1] 谭浩强.(2015).C++程序设计(第三版).清华大学出版社.

[2] 严蔚敏.(1997).数据结构(C语言版).清华大学出版社.

问题描述:将马随机放在国际象棋的 8X8 棋盘中的某个方格中,马按走棋规则进行移动。要求每个方格上只进入一次,走遍棋盘上全部 64 个方格。编制递归程序,求出马的行走路线 ,并按求出的行走路线,将数字 1,2,…,64 依次填入 8X8 的方阵输出之。测试数据:由读者指定可自行指定一个马的初始位置。实现提示:每次在多个可走位置中选择一个进行试探,其余未曾试探过的可走位置必须用适当结构妥善管理,以备试探失败时的“回溯”悔棋使用。并探讨每次选择位置的“最佳策略”,以减少回溯的次数。 背景介绍: 国际象棋为许多令人着迷的娱乐提供了固定的框架,而这些框架常独立于游戏本身。其中的许多框架都基于骑士奇异的L型移动规则。一个经典的例子是骑士漫游问题。从十八世纪初开始,这个问题就引起了数学家和解密爱好者的注意。简单地说,这个问题要求从棋盘上任一个方格开始按规则移动骑士,使之成功的游历国际象棋棋盘的64个方格,且每个方格都接触且仅接触一次。 可以用一种简便的方法表示问题的一个解,即将数字1,……,64按骑士到达的顺序依次放入棋盘的方格中。 一种非常巧妙的解决骑士漫游地方法由J.C.Warnsdorff于1823年给出。他给出的规则是:骑士总是移向那些具有最少出口数且尚未到达的方格之一。其中出口数是指通向尚未到达方格的出口数量。在进一步的阅读之前,你可以尝试利用Warnsdorff规则手工构造出该问题的一个解。 实习任务: 编写一个程序来获得马踏棋盘即骑士漫游问题的一个解。 您的程序需要达到下面的要求: 棋盘的规模是8*8; 对于任意给定的初始化位置进行试验,得到漫游问题的解; 对每次实验,按照棋盘矩阵的方式,打印每个格被行径的顺序编号。 技术提示: 解决这类问题的关键是考虑数据在计算机中的存储表示。可能最自然的表示方法就是把棋盘存储在一个8*8的二维数组board中。以(x,y)为起点时骑士可能进行的八种移动。一般来说,位于(x,y)的骑士可能移动到以下方格之一:(x-2,y+1)、(x-1,y+2)、(x+1,y+2)、(x+2,y+1)、(x+2,y-1)、(x+1,y-2)、(x-1,y-2)、(x-2,y-1)。但请注意,如果(x,y)的位置离某一条边较近,有些可能的移动就会把骑士移到棋盘之外,而这当然是不允许的。骑士的八种可能的移动可以用一个数组MoveOffset方便地表示出来: MoveOffset[0]=(-2,1) MoveOffset[1]=(-1,2) MoveOffset[2]=(1,2) MoveOffset[3]=(2,1) MoveOffset[4]=(2,-1) MoveOffset[5]=(1,-2) MoveOffset[6]=(-1,-2) MoveOffset[7]=(-2,-1) 于是,位于(x,y)的骑士可以移动到(x+MoveOffset[k].x, y+MoveOffset[k].y),其中k是0到7之间的某个整数值,并且新方格必须仍位于棋盘上。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值