此实例是解决寻找一张地图上(二维数组模拟的)从一个点到达另外指定的一个点需要的最少的移动步骤,其中地图上某点可能是障碍物不能跨越
地图信息约定:0表示空地,1表示目的地,-1表示障碍物,(0,0)表示的是默认起始点
一、深度优先搜索法
从初始起点开始对四个方向进行穷举,直到遇到目的点,递归的调用自己
#include<iostream>
using namespace std;
int min = 100000000;
int n, m;
void dfs(int a[30][30], int x, int y, int step)
{
if (a[x][y] == 1)//到达目的点
{
if (min > step)//移动步骤数比较
{
min=step;
}
return;
}
else
{
for (int i = 1; i <= 4; i++)//每个点都可能进行四个方向的移动
{
int tx = x, ty = y;
if (i == 1)//向上
tx = tx - 1;
else if (i == 2)//向下
tx = tx + 1;
else if (i == 3)//向左
ty = ty - 1;
else if (i == 4)//向右
ty = ty + 1;
if (tx < 0 || ty < 0 || tx >= n||ty >= m|| a[tx][ty] == -1 || a[tx][ty] == 2)//如果出界、遇到障碍物、或者在本条线路已经走过(每次走过的点都会被标记上2)
{
continue;
}
else
{
a[x][y] = 2;//每次走过的点都被标记上2
dfs(a, tx, ty, step + 1);//在进行下一步的移动
a[x][y] = 0;//再将刚刚标记走过的点该为空地,再次for循环,寻找其他方向
}
}
}
}
int main()
{
int a[30][30] = { 0 };
cout << "请输入地图的规模:行数 n = ";
cin >> n;
cout << "请输入地图的规模:列数 m = ";
cin >> m;
cout << "依次输入地图信息(1代表需要寻找的位置,0表示空地,-1代表障碍物!):" << endl;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (i == 0 && j == 0)
{
cout << "( 1,1 )为默认的起点无法修改!" << endl;
}
else
{
cout << "( " << i+1 << " , " << j+1 << " )" << " : ";
cin >> a[i][j];
}
}
}
dfs(a, 0, 0, 1);
cout << "最短的方法需要:" << min << " 步!" << endl;
return 0;
}
二、广度优先搜索法
广度优先搜索法主要是利用模拟队列(也可以用队列),一个头指针记录起始情况,尾指针一直往后增加合法的情况(入队列),与此同时头指针一直往后遍历(出队列),直到头指针和尾指针相。
拓展:这种模拟队列的搜索法与二叉树的层次遍历算法非常相似,可以随便画一个二叉树,然后层次遍历会发现从根节点开始入队,然后左孩子入队,再右孩子入队,同时这时根节点也出队列了。这时队列前端就变成了根结点的左孩子,且尾指针在遍历这个左孩子的左孩子、右孩子,同时头指针也在把这个左孩子出队列,然后尾指针遍历根节点右孩子的左、右孩子,此时头指针也在出队列跟根节点的右孩子…..这种算法充分利用了队列的线性表特点和先入先出规。
而此实例地图点的移动问题也是利用了队列的线性、先入先出的特点,从起始点开始对四周方向移动的进行逐一判断,如果合法则逐一入队,然后头指针模拟出队,再寻找刚刚入队的第一个元素,判断它的四种情况入队,再第一个出队,然后判断队列的头部(可能是第一批入队列的第二个元素,也可能是刚刚第二批的第一个元素)…..这正利用了队列的线性、先入先出的特点。
#include<iostream>
using namespace std;
struct qipan//结构体,用于存储坐标,和到达这个坐标的步骤数
{
int x;
int y;
int step;
};
int main()
{
int min = 1000;
int a[50][50] = { 0 }, b[50][50] = { 0 }, i, j, k, n, m;
qipan BFS[2501], *head, *end;//模拟队列的数组,头指针,尾指针
head = BFS;//头指针指向头部
end = head + 1;//初始化尾指针,注意是头部的后一个空位置,因为头部需要赋初值
cout << "请输入地图的行数 n = ";//确认地图的规模
cin >> n;
cout << "请输入地图的列数 m = ";
cin >> m;
cout << "请依次输入地图信息!(0代表空地,1代表需要到达的位置,-1代表障碍物)" << endl;
for (i = 0; i < n; i++)//按约定输入地图信息
{
for (j = 0; j < m; j++)
{
if (i == 0 && j == 0)
{
cout << "( " << i + 1 << " , " << j + 1 << " )" << "为默认起点!" << endl;
}
else
{
cout << "( " << i + 1 << " , " << j + 1 << " ) ";
cin >> a[i][j];
}
}
}
head->step = 0;//头部赋初值
head->x = 0;
head->y = 0;
while (end > head)
{
if (a[head->x][head->y] == 1)//到点了
{
if (min > head->step)//移动步骤数比较
min = head->step;
}
else
{
int tx, ty, tstep;
for (i = 1; i <= 4; i++)//这里是把四种走法中合法的情况都一一入队列,而不是深度优先的先选择其中一个再选择下一个
{//约定好从向右顺时针到向上
tx = head->x;
ty = head->y;
tstep = head->step + 1;
if (i == 1)
ty = ty + 1;//向右
else if (i == 2)
tx = tx + 1;//向下
else if (i == 3)
ty = ty - 1;//向左
else if (i == 4)
tx = tx - 1;//向上
if (tx<0 || tx>n || ty<0 || ty>m || a[tx][ty] == -1 || b[tx][ty] == 1)//出界、遇到障碍物、重复点
continue;
else
{
b[head->x][head->y] = 1;//将刚刚走过的点标记为1
end->x = tx;
end->y = ty;
end->step = tstep;//将这个合法的点放入模拟队列
end++;//尾指针后移
}
}
}
head++;
}
cout << min << endl;
return 0;
}
如输入
n=5,m=4
0 0 0 -1 0
0 0 0 0 0
0 0 0 -1 0
0 -1 1 0 0
0 0 0 0 0
输出 7
以我现在的水准觉得两则的最大的区别就是深度优先就是不撞南墙不回头(有点类似于回溯法),每次都是先选择当前步的一种合法的情况一直下一步到目的点或则不合法的的情况,然后回退至上一步选择当前步的另外一种情况。而广度优先则是把当前步所有的合法情况走一遍,然后把这当前步所有合法的下一步又进行下一步操作。这两个算法的命名也许就道破了两者的区别与联系。
第一次写博客,语言表达有误或者算法有错误还望指正!
刚刚入坑算法的萌新,还望各位路过的大佬多指点指点!【抱拳】【抱拳】