这两种搜索方法本质上都是基于蛮力法思路
这两种搜索方法对有向图和无向图都适用
constexpr auto MAXV = 1000; //定义最大顶点个数
//顶点信息
typedef struct
{
int no;
char data[MAXV];
}VertexType;
//矩阵信息
typedef struct
{
int edges[MAXV][MAXV];
int n, e;
VertexType vexs[MAXV];
}MGraph;
邻接矩阵示意图:
注意:边结点代表的是图中的边,而并非图中的结点;头结点才表示图中的结点;头结点身后所连接的为边结点
//边结点
typedef struct ANode
{
int adjvex; //这条边的终点
int weight; //这条边的权重
struct ANode* nextarc; //指向下一条与该结点相邻的边
} ArcNode;
//头结点
typedef struct VNode
{
string date; //顶点的其他信息
ArcNode* firstarc; //指向第一条边
};
//邻接表
typedef struct
{
VNode adjList[MAXV]; //头结点组成的数组
int n, e; //图中的顶点数n和边数e
}ALGraph;
邻接表示意图:(一般默认为出边表)
关键:
邻接矩阵的:
void DFS(MGraph g,int v)
{ int w;
cout<
邻接表的:
void DFS(ALGraph *G,int v)
{ ArcNode *p;
cout<adjlist[v].firstarc; //p指向顶点v的第一个邻接点
while (p!=NULL)
{ if (visited[p->adjvex]==0) //若p->adjvex顶点未访问,递归访问它
DFS(G,p->adjvex);
p=p->nextarc; //p指向顶点v的下一个邻接点
}
}
关键:
邻接矩阵的:
void BFS(MGraph g,int v) //邻接矩阵的DFS算法
{
queue qu; //定义一个队列qu
int visited[MAXV]; //定义存放结点的访问标志的数组
int w,i;
memset(visited,0,sizeof(visited)); //访问标志数组初始化
cout<
邻接表的:
void BFS(ALGraph *G,int v) //邻接表的DFS算法
{
ArcNode *p;
queue qu; //定义一个队列qu
int visited[MAXV]; //定义存放顶点的访问标志的数组
int w;
memset(visited,0,sizeof(visited)); //访问标志数组初始化
cout<adjlist[w].firstarc; //找顶点w的第一个邻接点
while (p!=NULL)
{ if (visited[p->adjvex]==0) //若当前邻接顶点未被访问
{ cout<adjvex]=1; //置该顶点已被访问的标志
qu.push(p->adjvex); //该顶点进队
}
p=p->nextarc; //找顶点w的下一个邻接点
}
}
printf("\n");
}
两种遍历方式都重点掌握邻接表的。
【问题描述】有如下8*8的迷宫图:
OXXXXXXX
OOOOOXXX
XOXXOOOX
XOXXOXXO
XOXXXXXX
XOXXOOOX
XOOOOXOO
XXXXXXXO
其中,O表示通路方块,X表示障碍方块。假设入口是位置(0,0),出口为右下角方块位置(7,7)。设计一个程序采用递归方法求指定入口到出口的一条迷宫路径。
深度优先的解法:
关键:
#include
#define MAxN 10 //最大迷宫大小
//问题表示
int n=8; //迷宫大小
char Maze[MAxN][MAxN]=
{ {'O','X','X','X','X','X','X','X'},
{'O','O','O','O','O','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','X','X','O','X','X','O'},
{'X','O','X','X','X','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','O','O','O','X','O','O'},
{'X','X','X','X','X','X','X','O'}
};
int H[4] = {0, 1, 0, -1}; //水平偏移量,下标对应方位号0~3
int V[4] = {-1, 0, 1, 0}; //垂直偏移量
void disppath() //输出一条迷宫路径
{
for (int i=0; i=0 && y>=0 && x(7,7)的一条迷宫路径
}
广度优先的解法:
关键:
#include
#define MAXQ 100 //队列大小
#define MAxN 10 //最大迷宫大小
//问题表示
int n=8; //迷宫大小
char Maze[MAxN][MAxN]=
{ {'O','X','X','X','X','X','X','X'},
{'O','O','O','O','O','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','X','X','O','X','X','O'},
{'X','O','X','X','X','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','O','O','O','X','O','O'},
{'X','X','X','X','X','X','X','O'}
};
int H[4] = {0, 1, 0, -1}; //水平偏移量,下标对应方位号0~3
int V[4] = {-1, 0, 1, 0}; //垂直偏移量
struct Position //队列元素类型
{
int x,y; //当前方块位置
int pre; //前驱方块的下标
};
Position qu[MAXQ]; //定义一个队列qu
int front=-1,rear=-1; //定义队头和队尾
void disppath(int front) //输出一条迷宫路径
{
int i,j;
for (i=0; i=0 && p2.y>=0 && p2.x(7,7)的一条迷宫路径
}
这个案例可以更好的理解广度优先遍历
小蓝有一个01矩阵。他打算将第一行第一列的 0 变为 2 。变化过程有传染性,每次 2 的上下左右四个相邻的位置中的 0 都会变成 2 。直到最后每个 2 的周围都是 1 或 2 结束。
请问,最终矩阵中有多少个 2 ?
以下是小蓝的矩阵,共 30 行 40 列。
0000100010000001101010101001001100000011
0101111001111101110111100000101010011111
1000010000011101010110000000001011010100
0110101010110000000101100100000101001001
0000011010100000111111001101100010101001
0110000110000000110100000000010010100011
0100110010000110000000100010000101110000
0010011010100110001111001101100110100010
1111000111101000001110010001001011101101
0011110100011000000001101001101110100001
0000000101011000010011111001010011011100
0000100000011001000100101000111011101100
0010110000001000001010100011000010100011
0110110000100011011010011010001101011011
0000100100000001010000101100000000000010
0011001000001000000010011001100101000110
1110101000011000000100011001001100111010
0000100100111000001101001000001010010001
0100010010000110100001100000110111110101
1000001001100010011001111101011001110001
0000000010100101000000111100110010101101
0010110101001100000100000010000010110011
0000011101001001000111011000100111010100
0010001100100000011000101011000000010101
1001111010010110011010101110000000101110
0110011101000010100001000101001001100010
1101000000010010011001000100110010000101
1001100010100010000100000101111111111100
1001011010101100001000000011000110110000
0011000100011000010111101000101110110001
思路:广度优先遍历,当队列为空时为循环终止条件,计算矩阵中2的个数
#include
#define MAXQ 100
#define MAxN 100
using namespace std;
int Maze[MAxN][MAxN] =
{
{0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,1,0,0,0,0,0,0,1,1},
{0,1,0,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,0,0,1,1,1,1,1},
{0,1,0,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,0,0,1,1,1,1,1},
{1,0,0,0,0,1,0,0,0,0,0,1,1,1,0,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,1,0,0},
{0,1,1,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1},
{0,0,0,0,0,1,1,0,1,0,1,0,0,0,0,0,1,1,1,1,1,1,0,0,1,1,0,1,1,0,0,0,1,0,1,0,1,0,0,1},
{0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,1,1},
{0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,1,1,1,0,0,0,0},
{0,0,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,0,1,1,1,1,0,0,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,0},
{1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,0,0,0,1,1,1,0,0,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1,0,1},
{0,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,0,1,1,1,0,1,0,0,0,0,1},
{0,0,0,0,0,0,0,1,0,1,0,1,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,1,0,0,1,1,0,1,1,1,0,0},
{0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,1,1,0,1,1,1,0,1,1,0,0},
{0,0,1,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,1,1,0,0,0,0,1,0,1,0,0,0,1,1},
{0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,1,1},
{0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
{0,0,1,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,1,0,1,0,0,0,1,1,0},
{1,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,1,1,0,0,1,0,0,1,1,0,0,1,1,1,0,1,0},
{0,0,0,0,1,0,0,1,0,0,1,1,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1},
{0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1},
{1,0,0,0,0,0,1,0,0,1,1,0,0,0,1,0,0,1,1,0,0,1,1,1,1,1,0,1,0,1,1,0,0,1,1,1,0,0,0,1},
{0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,0,1,0,1,1,0,1},
{0,0,1,0,1,1,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,0,1,1},
{0,0,0,0,0,1,1,1,0,1,0,0,1,0,0,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0,0,1,1,1,0,1,0,1,0,0},
{0,0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,1,1,0,0,0,0,0,0,0,1,0,1,0,1},
{1,0,0,1,1,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,1,0,0,0,0,0,0,0,1,0,1,1,1,0},
{0,1,1,0,0,1,1,1,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,0,1,0},
{1,1,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,1},
{1,0,0,1,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0},
{1,0,0,1,0,1,1,0,1,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,1,1,0,0,0,0},
{0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,1,1,1,1,0,1,0,0,0,1,0,1,1,1,0,1,1,0,0,0,1}
};
int H[4] = { 0, 1, 0, -1 }; //水平偏移量,下标对应方位号0~3
int V[4] = { -1, 0, 1, 0 }; //垂直偏移量
struct Position //队列元素类型
{
int x, y; //当前方块位置
};
Position qu[MAXQ]; //定义一个队列qu
int front = -1, rear = -1; //定义队头和队尾
//输出迷宫并输出迷宫中的2的个数
void print_2()
{
int count = 0;
for (int i = 0; i < 30; i++)
{
for (int j = 0; j < 40; j++)
{
cout << Maze[i][j];
if (Maze[i][j] == 2)
{
count++;
}
}
cout << endl;
}
cout << "迷宫中2的个数为:" << count << endl;
}
void BFS(int x, int y) //求从(x,y)出发的一条迷宫路径
{
Position p, p1, p2;
p.x = x; p.y = y; //建立入口结点
Maze[p.x][p.y] = 2; //改为2避免重复查找
rear++; qu[rear] = p; //入口方块进队
while (front != rear) //队不空循环
{
front++; p1 = qu[front]; //出队方块p1;
for (int k = 0; k < 4; k++) //试探p1的每个相邻方位
{
p2.x = p1.x + V[k]; //找到p1的相邻方块p2
p2.y = p1.y + H[k];
if (p2.x >= 0 && p2.y >= 0 && p2.x < 30 && p2.y < 40 && Maze[p2.x][p2.y] == 0)
{ //方块p2有效并且可走
Maze[p2.x][p2.y] = 2; //改为2避免重复查找
rear++; qu[rear] = p2; //方块p2进队
}
}
}
print_2();
}
int main()
{
int x = 0, y = 0;
BFS(x, y);
return 0;
}
输出结果:
如果两个相邻方格(上下左右四个方向相邻)内的数的最大公约数大于 1 ,则可以从其中一个方格移动到另一个方格,当然也可以从另一个方格移回第一个方格。
假设开始时站在第 r 行第 c 列,请问可以移动到方格图内的多少个方格?
样例输入
3 4
3 6 5 5
2 4 3 5
7 8 3 8
3 2
样例输出
5
思路:采用广度优先遍历,在将可行的方格放入到队列之前,计数+1
易错点+技巧点:
二维数组的初始化;
用同样尺寸的数组记录某个位置是否已经被访问过了;
没有记录路线的要求就可以不使用pre属性
二维数组的动态初始化:
//动态定义一个二维数组,并将其全部值初始化为0
int** map;
map = new int* [m];
for (int i = 0; i < m; i++)
{
int* row = new int[n];
for (int j = 0; j < n; j++)
{
row[j] = 1;
}
map[i] = row;
}
或者
//动态定义一个二维数组,并将其全部值初始化为0
int** map;
map = new int* [m];
for (int i = 0; i < m; i++)
{
map[i] = new int[n];
for (int j = 0; j < n; j++)
{
map[i][j] = 1;
}
}
使用memset
int** map = new int* [m];
for (int i = 0; i < m; ++i) {
map[i] = new int[n];
memset(map[i], 3, n * sizeof(int));
}
#include
#define MAXQ 100 //队列大小
#define MAxN 100 //最大迷宫大小
using namespace std;
int** map;
int** flag; //记录是否已经被访问了
int m;
int n;
int H[4] = { 0, 1, 0, -1 }; //水平偏移量,下标对应方位号0~3
int V[4] = { -1, 0, 1, 0 }; //垂直偏移量
struct Position //队列元素类型
{
int x, y;//当前方块位置
};
Position qu[MAXQ]; //定义一个队列qu
int front = -1, rear = -1; //定义队头和队尾
bool moreThanOne(int x, int y)
{
//两个数的最大公约数大于1,返回true
int theSmallOne = x < y ? x : y;
for (int i = 2; i <= theSmallOne; i++)
{
if (x % i == 0 && y % i == 0) return true;
}
return false;
}
void printMap()
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
cout << map[i][j]<<" ";
}
cout << endl;
}
}
void BFS(int x, int y)
{
int count = 0; //记录可以跳跃的个数
Position p, p1, p2;
p.x = x; p.y = y; //建立入口结点
rear++; qu[rear] = p;
count++;
while (front != rear) //队不空循环
{
front++; p1 = qu[front]; //出队方块p1;
for (int k = 0; k < 4; k++) //试探p1的每个相邻方位
{
p2.x = p1.x + V[k]; //找到p1的相邻方块p2
p2.y = p1.y + H[k];
if (p2.x >= 0 && p2.y >= 0 && p2.x < m && p2.y < n && moreThanOne(map[p2.x][p2.y], map[p1.x][p1.y]) && flag[p2.x][p2.y] != -1)
{
//方块p2有效并且可走
flag[p1.x][p1.y] = -1; //改为-1避免重复查找
rear++; qu[rear] = p2; //方块p2进队
count++;
}
}
}
printMap();
cout << count << endl;
}
int main()
{
//初始化矩阵
cin >> m >> n;
map = new int* [m]; //动态初始化二维数组这一步很重要,但是这一步最容易忘
flag = new int* [m];
for (int i = 0; i < m; i++)
{
int* row = new int[n];
int* flag_row = new int[n];
for (int j = 0; j < n; j++)
{
cin >> row[j];
flag_row[j] = 0;
}
map[i] = row;
flag[i] = flag_row;
}
//输入初始化的位置
int x, y;
cin >> x >> y;
BFS(x - 1, y - 1);
return 0;
}
运行结果: