记录贴膜小哥的学习笔记和代码。
前言
图的遍历有广度优先搜索和深度优先搜索。这里用一个迷宫游戏作为例子。
五乘五的方格是一个迷宫,四周和白色方块为墙,灰色为路径。0为起点,24为终点。
这个迷宫就是一个简单的图,通过遍历解决问题。
广度优先搜索
广度优先搜索是图的层次遍历。他有如下特点:
1–占用空间大
2–不进行回溯
3–常常使用队列实现
完整代码
1–先求出迷宫的邻接矩阵。25 * 25的数组
每一行为一个节点,每一列为与其他节点的关系,1为有连接
2–用队列存储待遍历的节点
3–用链表存储已遍历的节点
4–初始化起点和终点
5–遍历队列,查找节点连接的节点,判断是否遍历过,放入队列
#include<iostream>
#include<queue>
#include<list>
#include<algorithm>
bool data[25][25] = {
{ 0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },//0
{ 1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },//5
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0 },//10
{ 0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0 },//15
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0 },//20
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0 },//24
};
typedef struct str_data
{
int point;
std::string path;
}STR_Data;
int m_end;
std::queue<STR_Data> m_queue;
std::list<int> m_list;
//start:开始位置
//end:结束位置
void BFS_init(int start,int end)
{
char start_buf[10];
sprintf_s(start_buf,"%d",start);
STR_Data buff = {start,start_buf};
if (m_queue.empty())
{
m_queue.push(buff);//初始位置
}
m_end = end;
}
STR_Data BFS_run()
{
while (!m_queue.empty())
{
STR_Data buf = m_queue.front();
m_queue.pop();
if (buf.point == m_end)//匹配成功
{
printf("ok\n");
return buf;
}
else
{
m_list.push_back(buf.point);//标记遍历过的节点
//for (int i = buf.point + 1; i < 25; i++)
for (int i = 0; i < 25; i++)
{
if (i == buf.point)
continue;
if (data[buf.point][i] == 1)//有连接
{
auto t= std::find(m_list.cbegin(), m_list.cend(),i);
if (t == m_list.cend())
{
STR_Data str_buf;
char s[10];
str_buf.point = i;
sprintf_s(s,"%d",i);
str_buf.path = buf.path + "->"+s;
m_queue.push(str_buf);
}
}
}
}
}
return {0,"0"};
}
int main()
{
BFS_init(0,24);
STR_Data buf=BFS_run();
printf("%s\n", buf.path.c_str());
system("pause");
return 0;
}
深度优先搜索
深度优先搜索一直往下遍历,直到后面没有没遍历的节点后,回溯到上一个节点进行遍历。他有如下特点:
1–占用空间小
2–进行回溯
3–常常使用栈实现
完整代码
1–先求出迷宫的邻接矩阵。25 * 25的数组
2–用栈存储待遍历的节点
3–用链表存储已遍历的节点
4–初始化起点和终点
5–判断栈顶是否为终点,不是则循环递归,直到找到终点或者弹空。
#include<iostream>
#include<algorithm>
#include<list>
#include<stack>
bool data[25][25] = {
{ 0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },//0
{ 1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },//5
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0 },//10
{ 0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0 },//15
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0 },//20
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0 },//24
};
std::stack<int> m_stack;
std::list<int> m_list;
int m_end;
void DFS_init(int start ,int end)
{
if (m_stack.empty())
{
m_stack.push(start);
}
m_end = end;
}
int DFS_run()
{
if (m_stack.empty())
return 0;
int buf = m_stack.top();
if (buf == m_end)
{
char re[200] = {0};
printf("ok\n");
m_stack.pop();
sprintf_s(re, "%d",buf);
while (!m_stack.empty())
{
int b = m_stack.top();
char re2[200] = { 0 };
memcpy(re2,re,200);
sprintf_s(re,"%d -> %s",b,re2);
m_stack.pop();
}
printf("%s\n",re);
return 1;
}
else
{
m_list.push_back(buf);
for (int i = 0; i < 25; i++)
{
if (i == buf)
continue;
if (data[buf][i] == 1)//有连接
{
auto t = std::find(m_list.cbegin(), m_list.cend(), i);
if (t == m_list.cend())//新的节点
{
m_stack.push(i);
if (DFS_run() == 1)
return 1;
}
}
}
if(!m_stack.empty())
m_stack.pop();
return 0;
}
}
int main()
{
DFS_init(0, 24);
DFS_run();
system("pause");
return 0;
}
其他
广度优先搜索的一个优点是,搜索到的终点是最优解,也就是最短距离。而深度优先搜索随机找一个路径进行搜索,不一定是最优解。如迷宫图,0->11有两条路。
广度优先搜索的结果为最优路径
深度优先搜索的结果为长路径