深度优先搜索,广度优先搜索

Ⅰ 深度优先搜索:

先讲讲深度优先搜索的基本思想:

深度优先搜索主要用于树和图遍历,以及作为其他算法的基础

首先通过一个简单的例子理解一下深度优先搜索的核心思想:

题目 1 :

输出0-4这五个数字的全排列:

题目分析:

只需要将每种情况列举出来即可:利用for循环是最简单的

这里我们使用深度优先搜索:

代码:

#include <stdio.h>

//深度优先搜索
//理解深度优先搜索的最简单的方法 :全排列
//顺时针:右,下,左,上
//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 <stdio.h>

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个数字的时候

题目 2 :

从S到E的最短步数,并输出路径(坐标):

5
.S..*
..*..
*....
*.***
...E.

输入:

第一行,n表示为几行几列的行列式,第1+n行 输出地图

输出:

第一行,输出路径坐标,包括S和E的路线坐标,用箭头连接。

第二行,输出需要走几部。

核心思想:

使用我们刚刚学到的算法思想分析,路径有很多,所以我们将每一条路列举出来,最后保留步数最少的那一条路就行。

代码:

#include <stdio.h>
//迷宫问题
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<stdio.h>
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<stdio.h>
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;
}

查看队列内的元素

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值