数据结构课程设计——迷宫问题求解

前言

NEFU,计算机与控制工程学院,基于C/C++的数据结构 ,五个课程设计

环境

操作系统:Windows 10
IDE:Visual Studio Code、Dev C++ 5.11、Code::Blocks

课设目录

其他联系方式:

Gitee:@不太聪明的椰羊

B站:@不太聪明的椰羊

ps:一定要注明来意!

一、需求和规格说明

1.1 问题描述

        可以输入一个任意大小的迷宫数据,用非递归的方法求出一条走出迷宫的路径,并将路径输出。

 1.2 要求

        写明:存储结构、基本算法(可以使用程序流程图)、源程序、测试数据和结果、算法的时间复杂度、另外可以提出算法的改进方法。

二、设计

2.1 设计思想

        题目要求用非递归的方法求出一条走出迷宫的路径,并将路径输出,利用队列和广度优先遍历非递归的方法,可以直接找出一条最短路径。

题目基本要求:
BFSminroad()
        (1)为方便输出路径,从终点开始往起点找路径,将终点入队列,设置距离为0;利用数组pre[][]存每一个可以走的点的前一个点的坐标,终点的前一个点设为(0,0),作为输出路径时的结束判定;
        (2)取出队首存的点后将队首元素出队列,将此点能到达的点存入队列,并让这些点的距离为上一个点的距离+1,pre[][]存上一个点的坐标;
        (3)当前所在点的上下左右四个方向的点的情况(能到达则入队列)全部查找完成后,循环第二步,直到将起点添加到队列中。说明此时已经找到了最短路径;
        (4)通过while循环输出pre数组,循环条件为i != 0 && j != 0(终点设置的前点),即输出了最短路径。
         Tip:当两条路径一样长时,最后会输出在分叉点先入队列的方向所在路径,因为汇集点(汇集点之前路径一样长)对应的pre会存先入队列方向的点的坐标,并访问汇集点,另外一条路径不再存入队列,最后保存的是在分叉点先入队列的方向所在路径,我的for循环查找四个方向是顺序是左、下、右、上,所以相同最短路径中输出方向优先级为左下右上。
 时间复杂度:O(m*n)  (最坏情况,所有的点都能走,每个点都入队列。)  

题目拓展:利用栈和深度优先遍历递归的方法,可以将所有路径输出
DFSallroad()
        访问起点,将起点入栈,调用递归函数
DFS()
        取栈顶元素,判断是否为终点。若不是,继续探测栈顶元素的四个方向,探测到一个可达点后立刻访问该点,将其入栈并调用递归;若是,输出路径,回溯到上一个有多个(路径有分支)可达点的点,探测另外一条路径,回溯时清空访问信息并出栈。栈顶为终点时,栈底到栈顶的元素序列即为一条路径,将栈转存到另外一个栈中再输出,可实现从栈底到栈顶的输出。
时间复杂度:O(m*n)  (最坏情况,所有的点都能走,每个点都入栈。)

2.2 设计表示(存储结构)

typedef struct node
{
    int x,y;//地图坐标,从(1,1)开始
}mapnode;

三、解决方案

3.1 功能示意图

3.2 流程示意图

①建立地图信息流程


②BFS非递归求最短路径流程

​  

③DFS递归求所有路径流程

四、调试报告

输入“m=4 n=4”,地图信息,正确的起点(1,2)和终点(2,4);

输出最短路径,并询问是否输出所有路径,输入“y”

输出所有路径,并询问是否重新输入起点和终点

输入错误的起点(6,6)和终点(2,6)

提示坐标不符合,重新输入

重新输入正确的起点(1,2)和终点(4,4)

选择输出所有路径后,选择不重新输入起点和终点,选择重新输入地图

输入“m=6 n=6”,地图信息,正确的起点(1,2)和终点(4,6);

输出最短路径,并询问是否输出所有路径,输入“y”

输出所有路径,不再重新输入起点终点,不再重新输入地图,退出系统

五、代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <queue>
#define maxnum 1000
using namespace std;
typedef struct node
{
    int x,y;//地图坐标,从(1,1)开始
}mapnode;

int flag=0;//判断能否到终点
int m, n;//地图的行m与列n
int **map;//地图信息,0可以走,1不能走
int **visited;//访问信息,0表示未访问,1表示已经访问过
int minsize;//最短路径长度
stack<mapnode> s,mins;//深度遍历求所有路径,存路径和最短路径的栈
queue<mapnode> q;
mapnode in, out,**pre;//起点、终点、当前点的前一个点 的坐标
int movex[4]={0,1,0,-1};
int movey[4]={-1,0,1,0};//对应左、下、右、上移动

void createmap(int m, int n);//输入地图信息,创建访问信息
void visited0();// 清空访问信息
void deleteinfo(int m, int n);//删除申请的空间
void outputmap(int m, int n); // 输出地图
void outputroad(stack<mapnode> s1, int n); 输出路径(坐标),从栈底到栈顶的输出,n为控制符,传递形参,不影响实参
void outputvisited(int m, int n);// 输出访问数组,检查用
int pass(mapnode p);// 检查点是否可以走
int BFSminroad(queue<mapnode> &q);//广度遍历,非递归求最短路径,用visited存路径长度
void DFSallroad(stack<mapnode> &s); // 深度遍历,递归查找所有路径
void DFS(stack<mapnode> &s); // 深度遍历,递归

void createmap(int m, int n) // 输入地图信息,创建访问信息
{
    int i, j;
    //mapnode test;
    map = new int *[m+1];//m+1行
    visited = new int *[m + 1];
    //map = (int **)calloc(m+1,sizeof(int *));
    //visited = (int **)calloc(m + 1, sizeof(int *));
    for (i = 0; i <= m; i++)
    {
        map[i] = new int[n+1];// m+1行n+1列
        visited[i] = new int[n+1];
        //map[i] = (int *)calloc(n + 1, sizeof(int));
        //visited[i] = (int *)calloc(n + 1, sizeof(int)); 
    }
    for (i = 1; i <= m; i++)
      for (j = 1; j <= n; j++)
      {
        cin >> map[i][j];//输入地图信息
      }
    for (i = 1; i <= m; i++)//初始化访问数组为0 
     for (j = 1; j <= n; j++)
         visited[i][j] = 0;
}

void visited0()// 清空访问信息
{
    int i, j;
    for (i = 1; i <= m; i++)//初始化访问数组为0 
     for (j = 1; j <= n; j++)
         visited[i][j] = 0;
}

void deleteinfo(int m,int n)
{//删除申请的空间
    int i;
    for (i = 0; i <= m; i++)//从0开始,m+1个全释放
    {
        delete[] map[i];//释放一级指针
        delete[] visited[i];
        //delete[] pre[i];
        //free(map[i]);
        //free(visited[i]);
    }
    delete[] map;//释放二级指针
    delete[] visited;
    //delete[] pre;
    //free(map);
    //free(visited);
}

void outputroad(stack<mapnode> s1,int n)//传递形参,不影响实参
{//输出路径(坐标),从栈底到栈顶的输出,n为控制符
    stack<mapnode> s2;
    if(n==1)//n为控制符,n=1表示结合DFSallroad使用,每次输出时比较路径长度
    {
        if(s.size()-1<(unsigned int)minsize)//minsize>0,可强制转化为无符号数
        {
         mins = s;
         minsize = s.size()-1;
        }
    }//n!=1时,单纯作为输出路径函数使用,不比较最短路径长度,不更新最短路径
    while(!s1.empty())//将栈s1翻转存到栈s2中
    {//实现从栈底到栈顶的输出
     s2.push(s1.top());
     s1.pop();
    }
    while(!s2.empty())
    {
        cout << '(' << s2.top().x << ',' << s2.top().y << ')';
        s2.pop();
        if (!s2.empty())
        {
         cout << "->";
        }
    }
    cout << endl;
}

void outputmap(int m,int n)//输出地图
{
    cout << endl;
    int i, j;
    for (i = 1; i <= m; i++)
    {
        for (j = 1; j <= n; j++)
        {
            if(map[i][j]<2)
                cout << map[i][j] << ' ';
            else if(map[i][j]==2)//起点
                cout << "*"<<' ';
            else if(map[i][j]==3)//终点
                cout << "#"<<' ';
        }
        cout << endl;
    }
    cout << endl;
    cout << "注:地图坐标从(1,1)开始" << endl;
    cout << endl;
}

void outputvisited(int m,int n)//输出访问数组,检查用
{
    int i, j;
    cout << endl;
    for (i = 1; i <= m; i++)
    {
        for (j = 1; j <= n; j++)
        {
            cout << visited[i][j] << ' '; 
        }
        cout << endl;
    }
    cout << endl;
}

int pass(mapnode  p)//检查点是否可以走
{
    if (p.x<1 || p.x>m)//x坐标超出迷宫范围
    {
		return 0;
	}
	else if (p.y<1 || p.y>n)//y坐标超出迷宫范围
    {	
		return 0;
	}
	else if (!(visited[p.x][p.y] == 0))//已经走过该点
    {
		return 0;
	}
	else if (map[p.x][p.y] == 1)//该点是障碍
    {
		return 0;
	}
	else
        return 1; //可以走
}

int BFSminroad(queue<mapnode> &q)
{//广度遍历,非递归求最短路径,用visited存路径长度
    int i, j, temp;
    visited0();//清空访问信息
    pre = new mapnode *[m + 1]; // m+1行
    for (i = 0; i <= m; i++)
    {
        pre[i] = new mapnode[n+1];// m+1行n+1列 
    }
    q.push(out);//终点入队列,从终点往起点找路径
    pre[out.x][out.y].x= 0;
    pre[out.x][out.y].y= 0;//终点的前点设为(0,0)
    mapnode p,next;
    while(!q.empty())
    {
        p = q.front();//取队列首元素
        q.pop();// 队列首元素出队列
        if(p.x==in.x&&p.y==in.y)//队首为起点时查找结束
        {//能到达终点,输出路径和长度
            flag = 1;//能到达终点标志
            i = in.x;
            j = in.y; 
            while (i != 0 && j != 0)
            {
                printf("(%d, %d)", i, j);
                temp = i;
                i = pre[i][j].x;
                j = pre[temp][j].y;
                if (i != 0 && j != 0)
                {
                    cout << "->";
                }
            }
            cout << endl;
            cout << "最短路径长度为:" << visited[in.x][in.y] << endl;
            for (i = 0; i <= m; i++)//从0开始,m+1个全释放
            {
                delete[] pre[i];
            }
            delete[] pre;
            while(!q.empty())//清空队列 
            {
            	q.pop();
			}
            return 1;
        }
        for (i = 0; i < 4; i++)//循环遍历探测周围4个方向上的点
        {
            next.x = p.x + movex[i];
            next.y = p.y + movey[i];
            //printf("\n%d:%d,%d\n", i, next.x, next.y);
            if (pass(next)&&(next.x!=out.x||next.y!=out.y))// next为p的下一个点
            {
                visited[next.x][next.y] = visited[p.x][p.y] + 1; // 访问对应点,距离+1
                q.push(next);
                pre[next.x][next.y].x = p.x;
                pre[next.x][next.y].y = p.y;
            }
        }
    }
    cout << "找不到起点与终点间的路径,故无最短路径"<<endl;//while结束,队列空也没到终点
    for (i = 0; i <= m; i++) // 从0开始,m+1个全释放
    {
        delete[] pre[i];
    }
    delete[] pre;
    while(!q.empty())//清空队列 
    {
        q.pop();
	}
    return 0;
}

void DFSallroad(stack<mapnode> &s)
{
    visited0();
    flag = 0;//重置有通路标志符
    minsize = m * n;
    visited[in.x][in.y] = 1;//访问起点
    s.push(in);//起点入栈
    DFS(s);
}

void DFS(stack<mapnode> &s)
{//深度遍历,递归查找所有路径
    int i;
    mapnode p,next;//p工作点,next为p的下一个点
    p = s.top();//工作点为栈顶元素
    if(p.x==out.x&&p.y==out.y)//栈顶元素为终点
    {//能到达终点
        outputroad(s,1);//输出路径——从栈底到栈顶
        visited[p.x][p.y] = 0;//回退,恢复未访问状态
        s.pop();
        flag = 1;//能到达终点
        return;//找到终点,结束递归
    }
    for (i = 0; i < 4; i++)//循环遍历探测周围4个方向上的点
    {//i的不同,保证递归返回到分叉点时不再选择已选方向
        next.x = p.x + movex[i];
        next.y = p.y + movey[i];
        if(pass(next))//next为p的下一个点
        {//p.y = p.y + movey[i];// p.x = p.x + movex[i];
            visited[next.x][next.y] = 1; //访问对应点
            s.push(next);
            DFS(s);//递归
        }
    }
    s.pop();
    visited[p.x][p.y] = 0;//回退,恢复未访问状态
    if(s.empty())
    {
        if(flag==0)
        {
            cout << "找不到起点与终点间的路径";
        }
        else
        {
            cout << endl
                 << "所有路径已输出。最短路径长度为"<<minsize<<",如下:" << endl;
            outputroad(mins,0);
            while(!mins.empty())//清空最小路径栈
            {
            	mins.pop();
			}
        }
    
    }
}

int main()
{
    int flag2 = 1, flag3 = 1;
    int x;
    char y;
    while(flag2==1)//一级循环
    {
        cout << "请输入迷宫的行数和列数:" << endl;
        cin >> m >> n; // 迷宫第一个点坐标为(1,1)
        cout << "请输入迷宫的信息(0表示可通过,1表示障碍物):" << endl;
        createmap(m, n); // 输入地图信息
        cout << "注:地图坐标从(1,1)开始" << endl;
        cout << "请输入起点和终点:" << endl;
        while(flag3==1)//输入起点终点,二级循环
        {
            cin >> in.x >> in.y;
            cin >> out.x >> out.y;
            if(in.x<1||in.x>m||in.y<1||in.y>n||out.x<1||out.x>m||out.y<1||out.y>n||(in.x==out.x&&in.y==out.y))
            {//起点不能等于终点,起点终点不能为墙
                system("cls");
                cout << "你输入的地图如下" << endl
                     << "0表示可通过,1表示障碍物" << endl;
                outputmap(m, n);
                cout<<"起点终点坐标不符合,请重新输入:"<<endl;
                flag3 = 1;
                continue;//进入下一轮二级循环,重新输入起点终点
            }
            else if (map[in.x][in.y] == 1 || map[out.x][out.y] == 1)
            {
            	system("cls");
                cout << "你输入的地图如下" << endl
                     << "0表示可通过,1表示障碍物" << endl;
                outputmap(m, n);
                cout<<"起点终点坐标不符合,请重新输入:"<<endl;
                flag3 = 1;
                continue;//进入下一轮二级循环,重新输入起点终点
			}
            else
            {
                map[in.x][in.y] = 2;   // 起点
                map[out.x][out.y] = 3; // 终点
            }
            system("cls");
            cout << "你输入的地图如下" << endl
                 << "0表示可通过,1表示障碍物,*为起点,#为终点," << endl;
            outputmap(m, n);   // 打印地图
            printf("从(%d,%d)到(%d,%d)的一条最短的路径如下:\n",in.x,in.y,out.x,out.y);
            x = BFSminroad(q); // 输出最短路径
            if (x == 1)        // 有通路
            {
                cout << "是否输出所有路径?(Y or N)" << endl;
                cin >> y;
                if (y == 'y' || y == 'Y')
                {
                    system("cls");
                    cout << "你输入的地图如下" << endl
                         << "0表示可通过,1表示障碍物,*为起点,#为终点," << endl;
                    outputmap(m, n); // 打印地图
                    printf("从(%d,%d)到(%d,%d)的所有路径如下:\n",in.x,in.y,out.x,out.y);
                    DFSallroad(s);// 输出所有路径
                    cout << endl;
                }
                cout << "是否重新输入起点和终点?(Y or N)" << endl;
                cin >> y;
                if (y == 'y' || y == 'Y')
                {
                    map[in.x][in.y] = 0;   // 重置起点
                    map[out.x][out.y] = 0; // 重置终点
                    system("cls");
                    cout << "你输入的地图如下" << endl
                         << "0表示可通过,1表示障碍物" << endl;
                    outputmap(m, n); // 打印地图
                    cout << "请输入起点和终点:" << endl;
                    flag3 = 1;
                    continue;//进入下一轮二级循环,重新输入起点终点
                }
                cout << "是否继续输入新地图?(Y or N)" << endl;
                cin >> y;
                if(y=='y'||y=='Y')
                {
                    deleteinfo(m, n);//释放空间
                    flag2 = 1;
                    system("cls");
                    break;//结束二级循环
                }
                else
                {
                    deleteinfo(m, n);//释放空间
                    flag2 = 0;
                    system("cls");
                    break;//结束二级循环
                }
            }
            else//无通路
            {
                cout << "是否重新输入起点和终点?(Y or N)" << endl;
                cin >> y;
                if (y == 'y' || y == 'Y')
                {
                    map[in.x][in.y] = 0;   // 重置起点
                    map[out.x][out.y] = 0; // 重置终点
                    system("cls");
                    cout << "你输入的地图如下" << endl
                         << "0表示可通过,1表示障碍物" << endl;
                    outputmap(m, n); // 打印地图
                    cout << "请输入起点和终点:" << endl;
                    flag3 = 1;
                    continue;//进入下一轮二级循环,重新输入起点终点
                }
                cout << "是否继续输入新地图?(Y or N)" << endl;
                cin >> y;
                if(y=='y'||y=='Y')
                {
                    deleteinfo(m, n);//释放空间
                    flag2 = 1;
                    system("cls");
                    break;//结束二级循环
                }
                else
                {
                    deleteinfo(m, n);//释放空间
                    flag2 = 0;
                    system("cls");
                    break;//结束二级循环,退出系统
                }
            }  
        }
        
    }
    return 0;
}

/*
test 1:
输入迷宫的行数和列数:
4 4

输入迷宫的信息(0表示可通过,1表示障碍物):
1 0 1 1
1 0 0 0
1 0 1 0
1 0 0 0

输入起点和终点:      
1 2
2 4

查询所有路径

重新输入错误的起点、终点
6 6
2 6

提示起点、终点坐标不符合
重新输入正确的起点、终点
1 2
4 4

选择不重新输入起点和终点,选择重新输入地图

test2:
输入迷宫的行数和列数:
6 6

输入迷宫的信息(0表示可通过,1表示障碍物):
1 0 1 1 1 1
1 0 0 0 0 1
1 0 1 1 0 1
1 0 1 1 0 0 
1 0 1 1 0 1
1 0 0 0 0 1

输入起点和终点:      
1 2
4 6

选择输出所有路径

不再重新输入起点终点,不再重新输入地图

退出系统
*/

  • 71
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值