【Qt】数据结构迷宫问题可视化解决程序

数据结构课程设计抽到了用栈和队列解决迷宫问题的题目,小组突发奇想为什么不把结果使用可视化程序展现出来呢,因为DOS窗口里只会立即显示结果没有动态迷宫路径展示。
本篇博客实现了数据结构迷宫问题可视化解决程序。
先上一些效果图。
首先迷宫支持文件导入与手动输入。
文件导入(txt)
在这里插入图片描述

在这里插入图片描述
手动输入
在这里插入图片描述
点击生成迷宫与运行后会展示迷宫路径,动态,博客不便于展示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
同时路径也会显示在输出结果栏目中。
话不多说,上源代码。
Qt目录
在这里插入图片描述
mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QLineEdit>
#include <QMainWindow>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <typeinfo>
#include <iostream>
#include <sstream>
#include <vector>
#include <QDebug>
#include "QFileDialog"
#include <QtMath>
#include <QPainter>
#include <QPaintEvent>
#include <QPointF>
#include <QMessageBox>
#include <QPropertyAnimation>
#include <QTimer>
using namespace std;
#define OK 1
#define ERROR 0
#define MAXSIZE 100
typedef int Status;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

struct node {
    int x;
    int y;
    char orientation;
};

typedef struct snode {
    node pos;
    struct snode* next;
}snode;

typedef struct linkstack {
    snode* top;
    snode* bottom;
    int length;
}linkstack;

typedef struct
{
    int rx;				//位置x坐标
    int cy;				//位置y坐标
}Set;
typedef struct
{
    Set data[MAXSIZE];
    int length;			//路径长度
} PathType;//定义路径类型

struct Move
{
    int x;
    int y;
};//移动方向坐标


class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    bool GenerateMaze(int irow, int icol, QString text);
    void readInput();
    Status showPath(linkstack S);
    Status destroyStack(linkstack* S);
    ~MainWindow();

    Status creat(linkstack *S);
    bool empty(linkstack S);
    int size(linkstack S);
    Status push(linkstack *S, node pos);
    Status pop(linkstack *S);
    Status gettop(linkstack S, node &pos);
    void paintPath();
    void useStack();
    void useRecursion(int xis, int yis, int xes, int yes, PathType paths, int maze[8][8]);
private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

    void on_pushButton_3_clicked();

    void on_pushButton_4_clicked();
    void paintEvent(QPaintEvent *);

private:
    Ui::MainWindow *ui;
    int row = 0;
    int col = 0;
    int flag = false;
    int num = 0;
    int minpath = 100, b = 0;//求最短路径
    int maze[8][8] = {1};
    QString str;
    Move mov[4] = { {-1,0},{0,1},{1,0},{0,-1} };//四个移动方向,右下左上
    bool isCall = false;
    bool isDone = false;
    int pointCount = 1;
    int pathCount = 0;
    QLineEdit* input[MAXSIZE];
    QTimer *timer;
    QPointF point;
    QPointF points[MAXSIZE];
    QPointF path[MAXSIZE];
    QPointF currentPoint;
    QPointF nextPoint;
};
#endif // MAINWINDOW_H

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.setWindowTitle("迷宫可视化");
    w.setFixedSize(1200, 800);
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"


/*
            上N
        左W  x   东E
            下S
*/

Status MainWindow::creat(linkstack* S) {//创建一个空栈
    S->top = S->bottom = (snode*)malloc(sizeof(snode));
    if (!S->top) {
        ui->label_3->setText("申请空间失败!");
        return ERROR;
    }
    S->top->next = NULL;
    S->bottom->next = NULL;
    S->length = 0;
    return OK;
}
bool MainWindow::empty(linkstack S) {//判断栈是否为空
    return !S.length;
}
int MainWindow::size(linkstack S) {//返回栈的大小
    return S.length;
}
Status MainWindow::push(linkstack* S, node pos) {//元素入栈
    snode* newx = (snode*)malloc(sizeof(snode));//新结点
    if (!newx) {//创建失败
        ui->label_3->setText("申请空间失败!");
        return ERROR;
    }
    newx->pos = pos;//新结点的pos赋值
    newx->next = S->top->next;//入栈
    S->top->next = newx;
    if (!S->length)//length = 0
        S->bottom = S->bottom->next;
    S->length++;
    return OK;
}
Status MainWindow::pop(linkstack* S) {//弹出栈顶元素
    if (!S->length) {
        ui->label_3->setText("当前栈已经为空!");
        return ERROR;
    }
    snode* del = S->top->next;
    S->top->next = del->next;
    free(del);
    S->length--;
    return OK;
}
Status MainWindow::gettop(linkstack S, node& pos) {//获得栈顶元素 存到pos中
    if (!S.length) {
        ui->label_3->setText("当前栈已经为空!");
        return ERROR;
    }
    pos = S.top->next->pos;
    return OK;
}
Status MainWindow::showPath(linkstack S) {//输出当前栈的内容
    qDebug() << "showPath开始执行";
    if (!S.length) {
        ui->label_3->setText("空!");
        return ERROR;
    }
    snode* p = S.top->next;
    QString str = "栈:";
    while (p) {
        QString s1 = QString::number(p->pos.x);
        QString s2 = QString::number(p->pos.y);
        QString s3;
        if(p->next && s1 == QString::number(p->next->pos.x) && s2 < QString::number(p->next->pos.y))//比如<1,1,E>→<1,2,x>
            s3 = QString("E");
        if(p->next && s1 == QString::number(p->next->pos.x) && s2 > QString::number(p->next->pos.y))//比如<2,2,W>→<2,1,x>
            s3 = QString("W");
        if(p->next && s2 == QString::number(p->next->pos.y) && s1 < QString::number(p->next->pos.x))//比如<1,1,S>→<2,1,x>
            s3 = QString("S");
        if(p->next && s2 == QString::number(p->next->pos.x) && s1 > QString::number(p->next->pos.y))//比如<2,1,N>→<1,1,x>
            s3 = QString("N");

        if(!(p->next))
        {
            str += "<"+s1+","+s2 + ">";
        }
        else
        {
            str += "<"+s1+","+s2+","+s3+">";
        }
        path[pathCount] = QPointF(input[(p->pos.x - 1) * col + p->pos.y - 1]->pos().x() + 20,input[(p->pos.x - 1) * col + p->pos.y - 1]->pos().y() + 20);

        p = p->next;
        pathCount++;
        if(pathCount == 9)
            str += '\n';
        qDebug() << str;
    }
    ui->label_3->setText(str);
    qDebug() << "showPath执行完毕";
    return OK;

}
Status MainWindow::destroyStack(linkstack* S) {//销毁当前栈
    snode* del;
    while (S->top) {
        del = S->top;
        S->top = S->top->next;
        free(del);
    }
    S->top = S->bottom = NULL;
    return OK;
}
void MainWindow::readInput()//读取用户在lineedit里输入的数据
{
    int k = 0;
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            maze[i][j] = 1;
        }
    }
    for(int i = 0, m = 1; i < row; i++, m++)//显示lineedit
    {
        for (int j = 0, n = 1;j < col; j++, k++, n++) {
            if(j < col/2)
                maze[m][n] = input[k]->text().toInt();
            else
                maze[m][n] = input[k]->text().toInt();
        }
    }
    for (int i = 0; i < col*row; i++) {
        qDebug() << input[i]->text();
    }
    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 6; j++) {
            qDebug() << "maze[i][j]" << i << j << maze[i][j];
        }
    }
    qDebug() << "readInput执行完毕";
}
bool MainWindow::GenerateMaze(int irow, int icol, QString text)
{
    int k = 0;
    qDebug() << "GenerateMaze开始执行";
    QFont font("黑体", 20, 75); //第一个属性是字体(微软雅黑),第二个是大小,第三个是加粗(权重是75)
    row = irow;//迷宫高度是行数
    col = icol;//迷宫宽度是列数
    QRegExp regx("[0-1]+$");//lineedit只允许设置0或1
    for(int i = 0; i < row*col; i++)//生成row*col个lineedit
    {
        input[i] = new QLineEdit(QString::asprintf("0"), this);//最后一个参数一定为一个“父窗体”!
        input[i]->setMaxLength(1);
        input[i]->setAlignment(Qt::AlignHCenter);//内容居中显示
        input[i]->setFont(font);//字体设置
        if(i == 0)
            input[i]->setStyleSheet("background-color:green");
        if(i == row*col - 1)
            input[i]->setStyleSheet("background-color:red");
        QValidator *validator = new QRegExpValidator(regx, input[i]);
        input[i]->setValidator(validator);
        input[i]->hide();
        input[i]->show();
    }
    if(text != nullptr)
    {
        for (int i = 0; i < row*col; i++) {
            QString temp(text[i]);
            input[k++]->setText(temp);
        }
    }
    if(col%2)//col是奇数
    {
        int k = 0;
        int x = 600 - 20 - col/2*80;//控件初始位置,1200/2 = 600,20是靠近中轴线的lineedit与中轴线的距离,两个lineedit距离为40,中轴线左边有col/2个lineedit和空挡。
        int y = 40;//第一行lineedit和窗口顶部的距离
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                input[k++]->setGeometry(x + j*80, y + i*80,40,40);
            }
        }
    }
    else//col是偶数
    {
        int k = 0;
        int x = 600 - 20 - col/2*40 - (col/2 - 1)*40;
        int y = 40;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                input[k++]->setGeometry(x + j*80, y + i*80,40,40);
            }
        }
    }
    for (int i = 0; i < row*col; i++) {
        points[i] = QPointF(input[i]->pos().x(), input[i]->pos().y());
    }
    ui->pushButton->setEnabled(false);//一旦生成迷宫之后就不能再点击了,主要原因是input数组已经生成了,如果再点击,那么新生成的input也会出现在屏幕上,原先的linneedit不会消失
    qDebug() << "GenerateMaze执行完毕";
    return OK;
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->pushButton_2->setEnabled(false);
    ui->pushButton_4->setEnabled(false);

}
MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_clicked()//生成迷宫按钮按下时
{
    GenerateMaze(ui->spinBox_2->value(),ui->spinBox->value(), nullptr);//出现迷宫
    for (int i = 0; i < row*col; i++)
        input[i]->show();
    ui->pushButton_2->setEnabled(true);
}
void MainWindow::on_pushButton_2_clicked()//运行按钮按下时
{
    readInput();
    useStack();
//    PathType paths;
//    paths.length = 0;	//初始化路径长度
//    useRecursion(1, 1, row, col, paths, maze);
//    ui->label_4->setText(str);
//    if (b == 0)
//        ui->label_4->setText("空!");
}
void MainWindow::on_pushButton_3_clicked()//导入txt文件
{
    int irow = 0, icol = 0;
    QString text;
    QStringList infoList;
    //获取文件名
    QString fileName = QFileDialog::getOpenFileName(
                this,
                tr("导入文件"),
                "",
                tr("text(*.txt)"));
    if (fileName.isEmpty())
    {
        QMessageBox::warning(this, "错误", "打开文件失败!");
    }
    else
    {
        QFile file(fileName);
        //打开文件
        if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            qDebug()<<"无法打开!"<<endl;
        }
        //循环读取txt  并打印
        while(!file.atEnd())
        {
            QByteArray line = file.readLine();
            QString str(line);
            if(str.contains("\n"))
            {
                str = str.replace("\n" , "");
            }
            qDebug()<< str;

            icol = str.length() - 2;
            irow++;
            text += str.mid(1,icol);
        }

        irow -= 2;
        qDebug() << text;
        text = text.mid(icol,icol*irow);
        qDebug() << irow;
        qDebug() << icol;
        qDebug() << text;
        ui->pushButton_4->setEnabled(true);
    }
    GenerateMaze(irow, icol, text);

}
void MainWindow::on_pushButton_4_clicked()//导入txt文件后运行
{
    isCall = true;
    readInput();
    int sx = 1, sy = 1, ex = row, ey = col;//sx,sy是startx,starty即迷宫起始位置,对应的是迷宫的下标,ex,ey是endx,endy是endx,endy即迷宫终点位置。
    node now;//当前位置
    now.x = sx;
    now.y = sy;
    now.orientation = 'E';
    maze[sx][sy] = -1;//走过了

    linkstack s;
    creat(&s);创建一个空栈
    push(&s, now);//将now的信息亚展
    while (!empty(s)) {
        gettop(s, now);//获得栈顶元素 存到now中
        if (now.x == ex && now.y == ey)//now的x和y位置到达迷宫终点
            break;//终点

        if (!maze[now.x][now.y + 1]) {//右 为0表示可以走
            now.orientation = 'E';
            node next = now;
            next.y = now.y + 1;//向右走一步
            maze[next.x][next.y] = -1;//走过了置-1
            push(&s, next);
            continue;
        }
        //以下同理
        if (!maze[now.x + 1][now.y]) {//下
            now.orientation = 'S';
            node next = now;
            next.x = now.x + 1;
            maze[next.x][next.y] = -1;
            push(&s, next);
            continue;
        }
        if (!maze[now.x][now.y - 1]) {//左
            now.orientation = 'W';
            node next = now;
            next.y = now.y - 1;
            maze[next.x][next.y] = -1;
            push(&s, next);
            continue;
        }
        if (!maze[now.x - 1][now.y]) {//上
            now.orientation = 'N';
            node next = now;
            next.x = now.x - 1;
            maze[next.x][next.y] = -1;
            push(&s, next);
            continue;
        }
        pop(&s);
    }
    linkstack path;
    creat(&path);
    node step;
    while (!empty(s)) {
        gettop(s, step);
        push(&path, step);
        pop(&s);
    }//换成正向
    showPath(path);
    paintPath();
    qDebug() << "执行完毕";
    destroyStack(&s);
    destroyStack(&path);
}
void MainWindow::paintPath()//绘制路径函数
{
    timer = new QTimer(this);
    currentPoint = path[0];
    nextPoint = path[1];
    timer->start(50);
    qDebug() << "path0" << path[0];
    qDebug() << "path1" << path[1];
    qDebug() << "path2" << path[2];
    qDebug() << "path3" << path[3];
    qDebug() << "path4" << path[4];
    qDebug() << "path5" << path[5];
    qDebug() << "path6" << path[6];
    qDebug() << "path[][]" << path[pathCount - 1];
    connect(timer, &QTimer::timeout,this,[=](){
        static int i = 0;
        static int j = 0;
        static int k = 2;
        double x = path[0].x() + 10*i;
        qDebug() << "x" << x;
        qDebug() << "i" << i;
        double y = path[0].y()+ 10*j;
        qDebug() << "y" << y;
        qDebug() << "j" << j;
        update();
        if(x == nextPoint.x())
        {
            point.setX(x);
            point.setY(y);
            if(y <= nextPoint.y()){
                ++j;}
            else
                --j;
            if(y == nextPoint.y())
            {

                // --j;
                currentPoint = QPointF(nextPoint.x(),nextPoint.y());
                qDebug() << "currentPoint" << currentPoint;
                nextPoint = path[k++];
                qDebug() << "nextPoint" << nextPoint;
                qDebug() << "到达一个点了";
                pointCount++;
                if(x > nextPoint.x())
                {
                    --j;
                }
                if(x < nextPoint.x())
                {
                    --i;
                    --j;
                }
                if(y < nextPoint.y()){}
                if(nextPoint == QPointF(0,0)){
                    input[row*col - 1]->setStyleSheet("background-color:green");
                    qDebug() << "已经到达终点";
                    timer->stop();
                }
            }

        }
        if(y == nextPoint.y())
        {
            point.setX(x);
            point.setY(y);
            if(x <= nextPoint.x())
                ++i;
            else
                --i;
            if(x == nextPoint.x())
            {
                currentPoint = QPointF(nextPoint.x(),nextPoint.y());
                qDebug() << "currentPoint" << currentPoint;
                nextPoint = path[k++];
                qDebug() << "nextPoint" << nextPoint;
                qDebug() << "到达一个点了";
                pointCount++;
                if(x < nextPoint.x()){}
                if(y > nextPoint.y())
                    --i;
                if(y < nextPoint.y())
                {
                    --i;
                    --j;
                }
                if(nextPoint == QPointF(0,0)){
                    input[row*col - 1]->setStyleSheet("background-color:green");
                    qDebug() << "已经到达终点";
                    timer->stop();
                }
            }
        }
        qDebug() << x << "," << y;
    });

}

void MainWindow::useStack()
{
    isCall = true;
    int sx = 1, sy = 1, ex = row, ey = col;//sx,sy是startx,starty即迷宫起始位置,对应的是迷宫的下标,ex,ey是endx,endy是endx,endy即迷宫终点位置。
    node now;//当前位置
    now.x = sx;
    now.y = sy;
    now.orientation = 'E';
    maze[sx][sy] = -1;//走过了

    linkstack s;
    creat(&s);创建一个空栈
    push(&s, now);//将now的信息入栈
    while (!empty(s)) {
        gettop(s, now);//获得栈顶元素 存到now中
        if (now.x == ex && now.y == ey)//now的x和y位置到达迷宫终点
            break;//终点

        if (!maze[now.x][now.y + 1]) {//右 为0表示可以走
            now.orientation = 'E';
            node next = now;
            next.y = now.y + 1;//向右走一步
            maze[next.x][next.y] = -1;//走过了置-1
            push(&s, next);
            continue;
        }
        //以下同理
        if (!maze[now.x + 1][now.y]) {//下
            now.orientation = 'S';
            node next = now;
            next.x = now.x + 1;
            maze[next.x][next.y] = -1;
            push(&s, next);
            continue;
        }
        if (!maze[now.x][now.y - 1]) {//左
            now.orientation = 'W';
            node next = now;
            next.y = now.y - 1;
            maze[next.x][next.y] = -1;
            push(&s, next);
            continue;
        }
        if (!maze[now.x - 1][now.y]) {//上
            now.orientation = 'N';
            node next = now;
            next.x = now.x - 1;
            maze[next.x][next.y] = -1;
            push(&s, next);
            continue;
        }
        pop(&s);
    }
    linkstack path;
    creat(&path);
    node step;
    while (!empty(s)) {
        gettop(s, step);
        push(&path, step);
        pop(&s);
    }//换成正向
    showPath(path);
    paintPath();
    qDebug() << "执行完毕";
    destroyStack(&s);
    destroyStack(&path);
}

void MainWindow::useRecursion(int xis, int yis, int xes, int yes, PathType paths, int maze[8][8])
{
    int k, i, j;
    str = "迷宫第" + QString::number(num) + "条路径如下:";
    if (xis == xes && yis == yes)		//找到出口,输出路径
    {
        paths.data[paths.length].rx = xis;
        paths.data[paths.length].cy = yis;
        paths.length++; num++;
        if (paths.length < minpath)
        {
            minpath = paths.length;
            b = num;
        }
        for (k = 0; k < paths.length; k++)
            str += "(" + QString::number(paths.data[k].rx) + "," + QString::number(paths.data[k].cy) + ")" + " ";
    }
    else					//(xis,yis)不是出口
    {
        if (maze[xis][yis] == 0)//(xis,yis)可走
        {
            for (int m = 0; m < 4; m++)		// 找(xis, yis)的一个相邻位置(i, j)
            {
                i = xis + mov[m].x; j = yis + mov[m].y;
                paths.data[paths.length].rx = xis;
                paths.data[paths.length].cy = yis;
                paths.length++;
                maze[xis][yis] = -1;			//避免重复找路径
                useRecursion(i, j, xes, yes, paths, maze);
                paths.length--;			//回退一个位置
                maze[xis][yis] = 0;			//恢复(xis,yis)为可走
            }
        }
    }
}
void MainWindow::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing, true);
    p.setPen(QPen(Qt::green, 3));
    if(isCall)
    {
        p.drawPolyline(path,pointCount);
        p.drawLine(currentPoint,point);
    }
    else{}
}

UI布局
在这里插入图片描述
在这里插入图片描述
课程设计成绩最终也拿到了优秀,算是值了。不过由于时间匆忙,还有一些bug没有解决,但自己又懒,实在是不想再看咯。

以上 如果此篇博客对您有帮助欢迎点赞与转发 有疑问请留言或私信 2021/1/12

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值