首页 / 算法 / 基于Qt实现的算法可视化(汉诺塔)
基于Qt实现的算法可视化(汉诺塔)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了基于Qt实现的算法可视化(汉诺塔),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含9081字,纯文字阅读大概需要13分钟。
内容图文
![基于Qt实现的算法可视化(汉诺塔)](/upload/InfoBanner/zyjiaocheng/618/157b8b14d48b4080a0cdc0bd2e2de457.jpg)
接上篇基于Qt实现的算法可视化(棋盘覆盖、汉诺塔、旅行商),本篇主要给出汉诺塔问题的具体实现。
本篇给出汉诺塔移动的动态实现。虽然Qt有动画类,但是无奈太菜了,短时间内根本学不会,于是通过绘画类,即在短时间内不断重绘矩形,以此来实现动态的移动效果。另外,汉诺塔是一个递归问题,如果边递归边绘制,会出现绘制速度与递归速度不匹配的情况,从而让计数器发生混乱。所以最终思路与棋盘覆盖问题相近,即初始将汉诺塔问题中每一步的移动状态记录下来,然后再一步步动态实现这些移动。具体见代码注释。
头文件:
#ifndef HANOI_H
#define HANOI_H
#include <QWidget>
#include <QPushButton>
#include <QPixmap>
#include <QTimer>
#include <QPainter>
#include <QLineEdit>
#include <QLabel>
#include <stack>
class hanoi : public QWidget
{
Q_OBJECT
public:
explicit hanoi(QWidget *parent = 0);
void return_main(); //返回主界面
void setInit(); //生成汉诺塔上盘子
void setpath(int n, int a, int b, int c); //生成汉诺塔移动路径
//汉诺塔的移动通过短时间内不断重绘图像来实现
void hanioMove(); //汉诺塔的移动
void recMove_up(); //矩形向上移动
void recMove_hor(); //矩形水平移动
void recMove_down(); //矩形向下移动
protected:
void paintEvent(QPaintEvent *ev); //绘图事件,用于汉诺塔的移动
signals:
void return_sign(); //返回主界面的信号
public slots:
private:
//b1为“返回主界面”按钮,b2为“开始移动”按钮,b3为“确定”按钮
QPushButton b1, b2, b3;
QPixmap *pix; //汉诺塔图像
QLineEdit lineEdit; //行文本编辑器,用来读取k值
QLabel label; //用于显示行文本编辑器的输入提示
QTimer timer1, timer2, timer3; //矩形平移计时器
int num; //汉诺塔初始柱子上的盘子数
int x[10], y[10], w[10], h[10]; //每个盘子的横纵坐标和盘子的长度、高度
int moveNum, nowMove; //汉诺塔问题总的移动次数和当前移动
int path[100][2]; //汉诺塔问题移动路径,path[i][0]为起始柱,path[i][1]为目标柱
int id, st, des; //当前移动的盘子、起始柱子、目标柱子
std::stack<int> pillar[3]; //三根柱子的当前状态
bool moveFlag; //记录输入数据的正确性
};
#endif // HANOI_H
cpp文件:
#include "hanoi.h"
#include <QPen>
#include <QBrush>
#include <QDebug>
#include <QString>
#include <QtGlobal>
#include <QMessageBox>
#include <QFont>
hanoi::hanoi(QWidget *parent) : QWidget(parent), moveFlag(false)
{
//窗口设置
setWindowTitle("汉诺塔问题");
resize(1000, 650);
//按钮设置
b1.setParent(this); b2.setParent(this); b3.setParent(this); //设置父类
b1.setText("返回主界面"); b2.setText("开始移动"); b3.setText("确定"); //设置按钮内容
b1.move(725, 425); b2.move(725, 325); b3.move(830, 224); //设置按钮位置
b1.show(); b2.show(); b3.show(); //设置按钮显示在父类窗口
b1.resize(150, 40); b2.resize(150, 40); b3.resize(50, 27); //设置按钮大小
//设置按钮样式
b1.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:15px;");
b2.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:15px;");
b3.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;font:15px;");
//绘制初始背景
pix=new QPixmap(500, 500);
QPainter p(pix);
pix->fill(Qt::white); //背景填充
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::black));
p.drawRect(0, 400, 500, 100); //画底座
QPen pen(Qt::black); pen.setWidth(3);
p.setPen(pen);
for(int i=0; i<3; i++) //画线
p.drawLine(100+i*150, 100, 100+i*150, 400);
//输入提示界面的设置
label.setParent(this); //设置父类
label.setText("请输入一个1到6的整数:"); //设置提示内容
label.move(720, 125); //设置提示位置
label.resize(300, 100); //设置提示大小
label.setStyleSheet("font:15px;"); //设置提示界面样式
label.show(); //设置提示界面显示在父类窗口
//行文本编辑器的设置
lineEdit.setParent(this); //设置父类
lineEdit.move(725, 225); //设置编辑器位置
lineEdit.resize(100, 25); //设置编辑器大小
lineEdit.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px;"); //设置行文本编辑器样式
lineEdit.show(); //设置编辑器显示在父类窗口
//连接信号和槽函数
connect(&b1, &QPushButton::clicked, [=]()
{
//绘制初始背景
delete(pix);
pix=new QPixmap(500, 500);
QPainter p(pix);
pix->fill(Qt::white); //背景填充
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::black));
p.drawRect(0, 400, 500, 100); //画底座
QPen pen(Qt::black); pen.setWidth(3);
p.setPen(pen);
for(int i=0; i<3; i++) //画线
p.drawLine(100+i*150, 100, 100+i*150, 400);
timer1.stop(); timer2.stop(); timer3.stop(); //计时器停止
lineEdit.clear(); //文本编辑器清空
return_main(); //返回主界面
});
connect(&b2, &QPushButton::clicked, this, &hanoi::setInit); //对于直接点击移动的特殊处理
connect(&b2, &QPushButton::clicked, this, &hanoi::hanioMove); //汉诺塔移动
connect(&b3, &QPushButton::clicked, this, &hanoi::setInit); //生成汉诺塔上盘子
connect(&timer1, &QTimer::timeout, this, &hanoi::recMove_up); //连接timer1与向上移动
connect(&timer2, &QTimer::timeout, this, &hanoi::recMove_hor); //连接timer2与水平移动
connect(&timer3, &QTimer::timeout, this, &hanoi::recMove_down);//连接timer3与向下移动
}
//返回主界面的槽函数
void hanoi::return_main() {emit return_sign();}
//绘图事件
void hanoi::paintEvent(QPaintEvent *ev)
{
QPainter p(this);
p.drawPixmap(100, 50, *pix); //在(100,50)的位置绘制图像
QWidget::paintEvent(ev);
}
//生成汉诺塔上盘子
void hanoi::setInit()
{
//获取汉诺塔的盘子数
QString Q = lineEdit.text();
num = Q.toInt();
timer1.stop(); timer2.stop(); timer3.stop(); //终止上次移动
//数据初始化
moveNum=0; moveFlag=false;
for(int i=0;i<3;i++)
while(pillar[i].size())
pillar[i].pop();
//若输入小于等于0或大于6,则给出警告框
if(num<=0 || num>6)
{
QMessageBox qmb(QMessageBox::Warning, "警告", "您的输入有误,请重新输入!", QMessageBox::Close);
qmb.resize(200, 300);
moveFlag = false; //记录输入数据有误
qmb.exec();
}
else
{
//初始化图像
delete(pix);
pix=new QPixmap(500, 500);
//绘制初始背景
QPainter p(pix);
pix->fill(Qt::white); //背景填充
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::black));
p.drawRect(0, 400, 500, 100); //画底座
QPen pen(Qt::black); pen.setWidth(3);
p.setPen(pen);
for(int i=0; i<3; i++) //画线
p.drawLine(100+i*150, 100, 100+i*150, 400);
moveFlag=true;
//绘制汉诺塔盘子
for(int i=1; i<=num; i++)
{
pillar[0].push(i);
w[i]=100-(i-1)*10; h[i]=30;
x[i]=100-w[i]/2; y[i]=400-i*30;
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::black));
p.drawRect(x[i], y[i], w[i], h[i]);
}
update();
}
}
//向上移动
void hanoi::recMove_up()
{
QPainter p(pix);
//擦去矩形
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::white));
p.drawRect(x[id], y[id], w[id], h[id]);
//补充线
if(y[id]+h[id] > 100)
{
QPen pen(Qt::black); pen.setWidth(3);
p.setPen(pen);
p.drawLine(100+st*150, 100, 100+st*150, y[id]+h[id]);
}
//向上移动1像素
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::black));
y[id]-=1;
p.drawRect(x[id], y[id], w[id], h[id]);
update();
if(y[id] == 50) //若移动到纵坐标为50,则向上移动停止,开始水平移动
{
timer1.stop();
timer2.start(1);
}
}
//左右平移
void hanoi::recMove_hor()
{
QPainter p(pix);
//擦去矩形
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::white));
p.drawRect(x[id], y[id], w[id], h[id]);
if(st<des) //向右平移1像素
{
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::black));
x[id] += 1;
p.drawRect(x[id], y[id], w[id], h[id]);
update();
if(x[id] == 100 + des*150 - w[id]/2) //若移动到目标柱的上端,则平移停止,开始向下移动
{
timer2.stop();
timer3.start(1);
}
}
else //向左平移1像素
{
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::black));
x[id] -= 1;
p.drawRect(x[id], y[id], w[id], h[id]);
update();
if(x[id] == 100 + des*150 - w[id]/2) //若移动到目标柱的上端,则平移停止,开始向下移动
{
timer2.stop();
timer3.start(1);
}
}
}
//向下移动1像素
void hanoi::recMove_down()
{
QPainter p(pix);
//擦去矩形
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::white));
p.drawRect(x[id], y[id], w[id], h[id]);
//补充线
if(y[id]+h[id] > 100){
QPen pen(Qt::black); pen.setWidth(3);
p.setPen(pen);
p.drawLine(100+des*150, 100, 100+des*150, y[id]+h[id]);
}
//更新布局
p.setPen(QPen(Qt::white));
p.setBrush(QBrush(Qt::black));
y[id] += 1;
p.drawRect(x[id], y[id], w[id], h[id]);
update();
if(y[id] == 400 - 30*((int)pillar[des].size())) //若移动到目标位置,则平移停止
{
timer3.stop();
nowMove++;
if(nowMove < moveNum) //若移动未结束,则nowMove指向下一个移动路径
{
st=path[nowMove][0]; des=path[nowMove][1];
id = pillar[st].top();
timer1.start(1);
pillar[des].push(id); //将当前方块压入目标栈
pillar[st].pop(); //将当前方块从起始栈弹出
}
else //移动结束,将数据初始化
moveFlag = false;
}
}
//汉诺塔移动
void hanoi::hanioMove()
{
if(moveFlag)
{
//生成路径
moveNum=0;
setpath(num,0,1,2);
st=path[nowMove][0],des=path[nowMove][1];
id=pillar[st].top();
nowMove=0;
timer1.start(1);
pillar[des].push(id); //将当前方块压入目标栈
pillar[st].pop(); //将当前方块从起始栈弹出
}
}
//生成汉诺塔移动路径
void hanoi::setpath(int num, int a, int b, int c)
{
if(num == 0)
return;
setpath(num-1, a, c, b);
path[moveNum][0]=a; path[moveNum][1]=c; moveNum++;
setpath(num-1, b, a, c);
}
最终效果:
初始界面
汉诺塔初始状态
移动过程
内容总结
以上是互联网集市为您收集整理的基于Qt实现的算法可视化(汉诺塔)全部内容,希望文章能够帮你解决基于Qt实现的算法可视化(汉诺塔)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。