先讲讲深度优先搜索的基本思想:
深度优先搜索主要用于树和图遍历,以及作为其他算法的基础
首先通过一个简单的例子理解一下深度优先搜索的核心思想:
输出0-4这五个数字的全排列:
只需要将每种情况列举出来即可:利用for循环是最简单的
这里我们使用深度优先搜索:
#include
//深度优先搜索
//理解深度优先搜索的最简单的方法 :全排列
//顺时针:右,下,左,上
//int next[4][2]= {{0,1},{1,0},{0,-1}, {-1,0}};
int book[10];
int stack[10]; //给栈开十个空间,
//进行0-4,5个数字的全排列
void dfs(int step) {
if(step==6) {
//进行打印
for(int i=1; i<=5; i++) {
printf("%d ",stack[i]);
}
printf("\n");
//返回上一个函数
return ;
}
//将数字装进栈里
for(int i=0; i<=4; i++) {
if(book[i]==0) {
//标志:已经使用过此数字,数组的下标对应的就是数字
book[i]=1;
//将数字装进栈里面
stack[step]=i;
dfs(step+1);
book[i]=0;
}
}
}
int main() {
dfs(1);
/*
由于没有什么限制条件,所以 返回条件就是进行到了第几步
*/
return 0;
}
其实,就一句话:不撞南墙不回头。
理解深度优先搜索的关键在于解决:“当下如何做”和“下一步如何做”。
当前如何做对应:
这几个数字现在都在你的手上,你将它们一个一个往数组里面放,根据for循环,i的值放置,你也可以将要进行全排列的数字,放在一个数组里面,然后根据i的值,进行放置,
每次进行操作之前,先判断现在进行到了第几步,也就是判断放了几个数字,此处step的值代表准备放置第几个数字,
1.如果已经放置完毕,也就是等于n+1,就完成了一种情况,输出一种情况,返回上一个函数
2.如果没有执行完毕,就进入for循环,从头开始判断哪些牌你已经用过,此处使用一个数组book,下标代表进行排列的数字,如果使用过数组元素的值记为1,没有使用过就记为0,然后进行下一次操作。
如果返回上一个函数,那么就相当于撤回了一次操作,所以,我们将已经标记为1的数组,还原为0,继续进行判断,如果发现所有的数字使用过了,退出for循环,返回上一个函数,进行撤回操作,还原为0,继续进行判断,直到找到没用过的数字,进行下一步操作。
最后完成所有情况。
得出具体的模板:
void dfs(int step){
判断边界
尝试每一种可能 、
for(int i=0;i<=n;i++){
继续下一步
dfs(step+1);
}
}
如果输出任意几个数字的全排列呢?
我们定义一个结构体,定义两个数值:
1.判断数字是否被使用过
2.数字本身
#include
int stack[120];
int arrNum;
struct arr {
int book;
int num;
};
struct arr arr[100];
void dfs(int step) {
if (step == arrNum + 1) {
for (int i = 1; i <= arrNum; i++) {
printf("%d ", stack[i]);
}
printf("\n");
return;
}
for (int i = 0; i < arrNum; i++) {
if (arr[i].book == 0) {
arr[i].book = 1;
stack[step] = arr[i].num;
dfs(step + 1);
arr[i].book = 0;
}
}
}
int main() {
scanf("%d", &arrNum);
for (int i = 0; i < arrNum; i++) {
scanf("%d", &arr[i].num);
}
dfs(1);
return 0;
}
几个细节:
1.dfs初始值意思是处理第一个数字,此处和数组下标从零开始没有任何关系,输出的时候也是从1开始输出,因为stack数组书根据处理第几个数字,来分配到位置。
2。结束条件的判断,是处理第n+1个数字的时候
从S到E的最短步数,并输出路径(坐标):
5
.S..*
..*..
*....
*.***
...E.
第一行,n表示为几行几列的行列式,第1+n行 输出地图
第一行,输出路径坐标,包括S和E的路线坐标,用箭头连接。
第二行,输出需要走几部。
使用我们刚刚学到的算法思想分析,路径有很多,所以我们将每一条路列举出来,最后保留步数最少的那一条路就行。
#include
//迷宫问题
int n;
char mapp[50][50];
char book[50][50];
int ansStep = 9999;
//定义方向数组:
//顺时针:右,下,左,上
int next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int Xs, Ys, Xe, Ye;
typedef struct road {
int x;
int y;
} road;
road path[200];
road temp[200];
void dfs(int x, int y, int step) {
//进行条件判断
if (Xe == x && Ye == y) {
if (ansStep > step) {
ansStep = step;
for (int i = 0; i < ansStep; i++) {
path[i].x = temp[i].x;
path[i].y = temp[i].y;
}
}
return;
}
for (int i = 0; i < 4; i++) {
int kx = x + next[i][0];
int ky = y + next[i][1];
if (kx < 0 || ky < 0 || kx == n || ky == n) {
continue;
}
if (book[kx][ky] == 0 && mapp[kx][ky] == '.') {
book[kx][ky] = 1;
temp[step].x = kx;
temp[step].y = ky;
dfs(kx, ky, step + 1);
book[kx][ky] = 0;
}
}
}
int main() {
scanf("%d", &n);
//二维数组输入地图,最后会多一个'\0'
for (int i = 0; i < n; i++) {
scanf("%s", mapp[i]);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (mapp[i][j] == 'S') {
mapp[i][j] = '.' ;
Xs = i;
Ys = j;
}
if (mapp[i][j] == 'E') {
mapp[i][j] = '.';
Xe = i;
Ye = j;
}
}
}
//就像step,我们需要知道每次到了哪一个点
//从(Xs,Ys) 开始
book[Xs][Ys] = 1;
dfs(Xs, Ys, 0);
printf("需要:%d\n", ansStep);
printf("(%d,%d)->", Xs + 1, Ys + 1);
for (int i = 0; i < ansStep; i++) {
printf("(%d,%d)", path[i].x + 1, path[i].y + 1);
if (i != ansStep - 1) {
printf("->");
}
}
return 0;
}
1.由于输入方式,所以二维数组从(0,0)开始,
2.完成一种情况之后判断是否更新最小值的同时,记得将road数组更新一遍
同样的我们用迷宫问题举一个例子:
广度优先搜索不同于深度优先搜索的地方在于,它是通过一层一层的方式进行寻找,广度优先搜索是将四个方向上可以走的路作为可以延伸的路,作为一层,判断这层有没有符合满足要求的点,如果没有继续寻找,直到队列为空,也就是,没有符合条件的点,,当然遍历的时候按照你规定的顺序,是顺时针或者逆时针。
规定一个mapp[50][50];队列最大规定[2500];
#include
int next[4][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };
struct queue {
int x;
int y;
int s;
};
struct queue que[2500];
int book[51][51];
char mapp[51][51];
int main() {
int Xe, Ye, Xs, Ys;
int n;
scanf("%d",&n);
int flag = 0;
//初始化队列
for (int i = 0; i < n; i++) {
scanf("%s",mapp[i]);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (mapp[i][j] == 'S') {
mapp[i][j] = '.' ;
Xs = i;
Ys = j;
}
if (mapp[i][j] == 'E') {
mapp[i][j] = '.';
Xe = i;
Ye = j;
}
}
}
//初始化队列
int head = 0;
int tail = 0;
que[tail].x = Xs;
que[tail].y = Ys;
que[tail].s = 0;
tail++;
book[Xs][Ys] = 1;
while (head < tail) {
for (int k = 0; k < 4; k++) {
// 寻找下一步
int tx = que[head].x + next[k][0];
int ty = que[head].y + next[k][1];
if (tx < 0 || tx >= n || ty < 0 || ty >= n) { //判断出界
continue;
}
if (mapp[tx][ty] == '*' || book[tx][ty] == 1) { //判断是否是障碍物或已走过
continue;
}
que[tail].x = tx; //入队
que[tail].y = ty;
que[tail].s = que[head].s + 1;//步数+1
tail++;//扩展队列,后移
book[tx][ty] = 1;//标记
if (tx == Xe && ty == Ye) { //判断是否到达终点
flag = 1;
break;
}
}
//多层循环退出,so,用了flag
if (flag == 1) {
break;
}
head++;//出队
}
printf("最短步数:%d\n", que[tail - 1].s);//tail上没有数据
return 0;
}
主要在于理解 这层层递进,步步为营的意思,下一次进行判断的点,开始于上次最后一个点,进行一步一步判断
#include
int next[4][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };
struct queue {
int x;
int y;
int s;
};
struct queue que[2500];
int book[51][51];
char mapp[51][51];
int main() {
int Xe, Ye, Xs, Ys;
int n;
scanf("%d", &n);
int flag = 0;
//初始化队列
for (int i = 0; i < n; i++) {
scanf("%s", mapp[i]);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (mapp[i][j] == 'S') {
mapp[i][j] = '.' ;
Xs = i;
Ys = j;
}
if (mapp[i][j] == 'E') {
mapp[i][j] = '.';
Xe = i;
Ye = j;
}
}
}
//初始化队列
int head = 0;
int tail = 0;
que[tail].x = Xs;
que[tail].y = Ys;
que[tail].s = 0;
tail++;
book[Xs][Ys] = 1;
while (head < tail) {
for (int k = 0; k < 4; k++) {
// 寻找下一步
int tx = que[head].x + next[k][0];
int ty = que[head].y + next[k][1];
if (tx < 0 || tx >= n || ty < 0 || ty >= n) { //判断出界
continue;
}
if (mapp[tx][ty] == '*' || book[tx][ty] == 1) { //判断是否是障碍物或已走过
continue;
}
que[tail].x = tx; //入队
que[tail].y = ty;
que[tail].s = que[head].s + 1;//步数+1
tail++;//扩展队列,后移
book[tx][ty] = 1;//标记
if (tx == Xe && ty == Ye) { //判断是否到达终点
flag = 1;
break;
}
}
for (int i = 0; i < 10; i++) {
printf("%d %d %d\n", que[i].x, que[i].y, que[i].s);
}
printf("\n");
//多层循环退出,so,用了flag
if (flag == 1) {
break;
}
head++;//出队
}
printf("最短步数:%d\n", que[tail - 1].s);//tail上没有数据
for (int i = 0; i < 10; i++) {
printf("%d %d %d\n", que[i].x, que[i].y, que[i].s);
}
return 0;
}
查看队列内的元素