目录
图论
图的结构
图是一种非线性结构
它有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 运行结果
缺陷
可能终点就在旁边,但最终得到的路径会绕很大一个弯
如果迷宫比较大,或者无限大,那终点可能费很大力气或者永远找不到
简而言之,它不够智能