【数据结构与算法】图及深度优先

目录

图论

图的结构

图的组成

图的分类

深度优先

愣头青

实例

1 精心准备一个数组

2 坐标 方向 辅助草图

3 定义类,封装功能

4 初始化

5 寻找出口

6 输出路径

7 完整代码

8 运行结果

缺陷


图论

图的结构

图是一种非线性结构

它有n个直接前驱,n个直接后驱

图的组成

由两个部分组成,一部分叫 节点 的集合,另一部分叫 的集合

图的分类

  • 有向图:有方向的图

  • 无向图:无方向的图

  • 有限图:节点和边都有限

  • 简单图:没有环和多重边

反例:

 

  • 完全图:任意两个点都有一条边相连

  • 二分图:顶点集分为两组,而且同组的顶点不相邻

  • 途径:两顶点之间的通路

  • 边与弧:有方向的边角弧,没方向的叫边

深度优先

愣头青

沿着一个固定的方向走,有岔入口才会又一次选择方向(根据自己设置的方向优先级选择)

走着走着要是碰到死胡同,就会退到上一个岔路口,重新选择方向

走过的路不会再走了(除了返回的时候)

(最后得到的路径,可能会因设置的方向的不同而不同)

实例

1 精心准备一个数组

#define MAP_ROW 10
#define MAP_COL 10
//这是一个迷宫
int mapArr[MAP_ROW][MAP_COL] = {
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },//0
    { 1, 0, 0, 0, 0, 0, 1, 1, 0, 1 },
    { 1, 0, 1, 1, 1, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 1, 1, 0, 0, 1 },
    { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1 },//4
    { 1, 0, 0, 0, 0, 1, 0, 1, 0, 1 },
    { 1, 0, 1, 1, 0, 1, 0, 0, 0, 1 },
    { 1, 0, 1, 1, 0, 1, 0, 1, 1, 1 },
    { 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },//9
};

2 坐标 方向 辅助草图

struct pointCoord{
    int row;
    int col;
};
//我们需要的方向是常量,而且只有四个
//枚举显然是个不错的选择
enum futureDir{
    e_up,   //上
    e_left, //左
    e_down, //下
    e_right //右
};
struct mapDraft{
    int val;
    futureDir dir;
    bool isPass;
};

3 定义类,封装功能

class SimpleMap{
private:
    //这是二维数组 
    vector<vector<mapDraft>> m_mapDraft;
    pointCoord m_begin; //走迷宫,总要有个起点
    pointCoord m_end;   //起码也要有个终点
    stack<pointCoord> m_Path;//STL 栈,直接使用,多是件美事啊
    bool canMove(int row, int col);
public:
    void searchEnd();//寻找终点的
    void showPath(); //打印路径的
    SimpleMap();     //用来初始化一些数据成员的
};

4 初始化

SimpleMap::SimpleMap()
{
    int i, j;
    m_begin = { 1, 1 };//起点啦
    m_end = { 8, 8 };  //终点啦
​
    m_mapDraft.resize(MAP_ROW);//这是二维数组的行数
    //注意:不要在类中定义的时候直接初始化,如下
    //vector<vector<mapDraft>> m_mapDraft(MAP_ROW);
    //编译器分不清你是要定义一个函数还是啥,众所周知,编译器是不粘锅的,它应该会报错的
    for (i = 0; i < MAP_ROW; ++i)
    {
        m_mapDraft[i].resize(MAP_COL);
        for (j = 0; j < MAP_COL; ++j)
        {
            //将辅助地图每个坐标的值,捏成真地图的样子
            m_mapDraft[i][j].val = mapArr[i][j];
            //走迷宫自然要有个方向,总不能乱走啦
            m_mapDraft[i][j].dir = e_up;
            //还没开始的时候,每个坐标先看作没走过
            m_mapDraft[i][j].isPass = false;
        }
    }
    m_Path.push(m_begin);//m_Path 是记录路线的,起点当然要记录啦
}

5 寻找出口

//前面能不能走,自然是要判断一下的
bool SimpleMap::canMove(int row, int col)
{
    //越界了,自然不能走
    if (row < 0 || col < 0 || row >= MAP_ROW || col >= MAP_COL)
    {
        return false;
    }
    else if (m_mapDraft[row][col].val == 0 && m_mapDraft[row][col].isPass == false)
    {
        return true;
    }
    return false;
}
//不会正经的走,而是推演路线
void SimpleMap::searchEnd()
{
    //nearpoint 是颗“小石头”,功能“投石问路”
    //探路的事情就不要亲历亲为了
    pointCoord nearPoint = m_begin;
    while (true)
    {
        //正常人走了都要有个方向
        //而程序要更严谨一点
        //不仅要有方向,还要说明那个方向会优先被选择,当然假如这个方向走不通的话,还要
        //给程序指明另一个方向
        //我这里规定的顺序是:上,左,下,右(逆时针)
        switch (m_mapDraft[nearPoint.row][nearPoint.col].dir)
        {//m_mapDraft这个辅助地图中记录着要走的方向
        case 0://上
            m_mapDraft[nearPoint.row][nearPoint.col].dir = e_left;
            //在该位置,上这个方向已经走过了,自然要把它划掉
            //然后记录下一个方向
            //假如将来撞到了“南墙”,回到上一个岔路口,就不会选择旧路了
            if (canMove(nearPoint.row - 1, nearPoint.col))
            {
                //标记这里走过
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord temp = { nearPoint.row - 1, nearPoint.col };
                m_Path.push(temp);//记录位置
                nearPoint = temp; //“捡回石头”
            }
            break;
        case 1://左
            //很显然,这基本是粘贴复制,毕竟逻辑是一样的
            m_mapDraft[nearPoint.row][nearPoint.col].dir = e_down;
            if (canMove(nearPoint.row, nearPoint.col - 1))
            {
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord temp = { nearPoint.row, nearPoint.col - 1 };
                m_Path.push(temp);
                nearPoint = temp;
            }
            break;
        case 2://下
            m_mapDraft[nearPoint.row][nearPoint.col].dir = e_right;
            if (canMove(nearPoint.row + 1, nearPoint.col))
            {
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord temp = { nearPoint.row + 1, nearPoint.col };
                m_Path.push(temp);
                nearPoint = temp;
            }
            break;
        case 3://右
            if (canMove(nearPoint.row, nearPoint.col + 1))
            {
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord temp = { nearPoint.row, nearPoint.col + 1 };
                m_Path.push(temp);
                nearPoint = temp;
            }
            //四个方向都走不了,说明进入了死胡同,这就要返回上一个岔入口了
            else {
                //当前位置当然要标记为走过
                //不然下一个循环,又会往右走,但又不会标记为走过,从而变成死循环
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord tempPoint = m_Path.top();//栈顶是前一步的位置
                
                //把回退的路线打印一下
                cout << "Send back row = " << tempPoint.row
                    << ", col = " << tempPoint.col << endl;
                
                m_Path.pop();//m_Path 最终是要记录着一条可行的路线的,所以走不通的路,就删除掉
                //更何况我们可能还要回退
                if (!m_Path.empty()) {
                    //我们的探路工具还是要“捡回来”
                    nearPoint = m_Path.top();
                }
                else {
                    cout << "cannot find target" << endl;
                }
            }
            break;
        }
        //如果还有机会找到终点,m_Path 是不会空的
        if (m_Path.empty()) {
            break;
        }
        //找到终点,退出循环
        if (nearPoint.row == m_end.row && nearPoint.col == m_end.col) {
            break;
        }
    }
}

6 输出路径

输出走的路径,就能检测一下代码的正确性了

void SimpleMap::showPath()
{
    cout << "-------------------------\n";
    pointCoord tempPoint = m_Path.top();
    while (!m_Path.empty())
    {
        //第一次循环前,m_Path 的栈顶元素应该是终点
        tempPoint = m_Path.top();
        cout << "route row = " << tempPoint.row
            << ", col = " << tempPoint.col << endl;
        m_Path.pop();
    }
}

7 完整代码

#ifndef _FIGURE_BOX_
#define _FIGURE_BOX_
#include <iostream>
#include <stack>
#include <vector>
#include <cstdlib>
using namespace std;
​
#define MAP_ROW 10
#define MAP_COL 10
​
int mapArr[MAP_ROW][MAP_COL] = {
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },//0
    { 1, 0, 0, 0, 0, 0, 1, 1, 0, 1 },
    { 1, 0, 1, 1, 1, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 1, 1, 0, 0, 1 },
    { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1 },//4
    { 1, 0, 0, 0, 0, 1, 0, 1, 0, 1 },
    { 1, 0, 1, 1, 0, 1, 0, 0, 0, 1 },
    { 1, 0, 1, 1, 0, 1, 0, 1, 1, 1 },
    { 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },//9
};
​
struct pointCoord{
    int row;
    int col;
};
enum futureDir{
    e_up,   //上
    e_left, //左
    e_down, //下
    e_right //右
};
struct mapDraft{
    int val;
    futureDir dir;
    bool isPass;
};
​
class SimpleMap{
private:
    vector<vector<mapDraft>> m_mapDraft;
    pointCoord m_begin;
    pointCoord m_end;
    stack<pointCoord> m_Path;
    bool canMove(int row, int col);
public:
    void searchEnd();
    void showPath();
    SimpleMap();
};
SimpleMap::SimpleMap() //初始化
{
    int i, j;
    m_begin = { 1, 1 };
    m_end = { 8, 8 };
​
    m_mapDraft.resize(MAP_ROW);
    for (i = 0; i < MAP_ROW; ++i)
    {
        m_mapDraft[i].resize(MAP_COL);
        for (j = 0; j < MAP_COL; ++j)
        {
            m_mapDraft[i][j].val = mapArr[i][j];
            m_mapDraft[i][j].dir = e_up;
            m_mapDraft[i][j].isPass = false;
        }
    }
​
    m_Path.push(m_begin);
}
void SimpleMap::showPath() //输出路径
{
    cout << "-------------------------\n";
    pointCoord tempPoint = m_Path.top();
    while (!m_Path.empty())
    {
        tempPoint = m_Path.top();
        cout << "route row = " << tempPoint.row
            << ", col = " << tempPoint.col << endl;
        m_Path.pop();
    }
}
void SimpleMap::searchEnd()
{
    pointCoord nearPoint = m_begin;
    while (true)
    {
        switch (m_mapDraft[nearPoint.row][nearPoint.col].dir)
        {
        case 0://上
            m_mapDraft[nearPoint.row][nearPoint.col].dir = e_left;
            if (canMove(nearPoint.row - 1, nearPoint.col))
            {
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord temp = { nearPoint.row - 1, nearPoint.col };
                m_Path.push(temp);
                nearPoint = temp;
            }
            break;
        case 1://左
            m_mapDraft[nearPoint.row][nearPoint.col].dir = e_down;
            if (canMove(nearPoint.row, nearPoint.col - 1))
            {
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord temp = { nearPoint.row, nearPoint.col - 1 };
                m_Path.push(temp);
                nearPoint = temp;
            }
            break;
        case 2://下
            m_mapDraft[nearPoint.row][nearPoint.col].dir = e_right;
            if (canMove(nearPoint.row + 1, nearPoint.col))
            {
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord temp = { nearPoint.row + 1, nearPoint.col };
                m_Path.push(temp);
                nearPoint = temp;
            }
            break;
        case 3://右
            if (canMove(nearPoint.row, nearPoint.col + 1))
            {
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord temp = { nearPoint.row, nearPoint.col + 1 };
                m_Path.push(temp);
                nearPoint = temp;
            }
            else {
                m_mapDraft[nearPoint.row][nearPoint.col].isPass = true;
                pointCoord tempPoint = m_Path.top();
                cout << "Send back row = " << tempPoint.row
                    << ", col = " << tempPoint.col << endl;
                m_Path.pop();
                if (!m_Path.empty()) {
                    nearPoint = m_Path.top();
                }
                else {
                    cout << "cannot find target" << endl;
                }
            }
            break;
        }
        if (m_Path.empty()) {
            break;
        }
        if (nearPoint.row == m_end.row && nearPoint.col == m_end.col) {
            break;
        }
    }
}
bool SimpleMap::canMove(int row, int col)
{
    if (row < 0 || col < 0 || row >= MAP_ROW || col >= MAP_COL)
    {
        return false;
    }
    else if (m_mapDraft[row][col].val == 0 && m_mapDraft[row][col].dir == false)
    {
        return true;
    }
    return false;
}
#endif /*_FIGURE_BOX_*/

8 运行结果

缺陷

可能终点就在旁边,但最终得到的路径会绕很大一个弯

如果迷宫比较大,或者无限大,那终点可能费很大力气或者永远找不到

简而言之,它不够智能

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

维他命C++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值