11 搜索算法的深层理解(图)

1、图与搜索

简单地说,图就是由一些小圆点(称为顶点)和连接这些小圆点的直线(称为边)组成的。例如上图是由五个顶点(编号为1、2、3、4、5)和5条边(1-2、1-3、1-5、2-4、3-5)组成。

现在我们从1号顶点开始遍历这个图,遍历就是指把图的每一个顶点都访问一次。使用深度优先搜索来遍历这个图将会得到如下的结果。

图中每个顶点右上方的数就表示这个顶点是第几个被访问到——时间戳。

 深度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点;当没有未访问过的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有的顶点都被访问过。显然,深度优先遍历是沿着图的某一条分支遍历直到末端,然后回溯,再沿着另一条进行同样的遍历,直到所有的顶点都被访问过为止。

存储一个图最常用的方法是使用一个二维数组e来存储。

上图二维数组中第i行第j列表示的就是顶点i到顶点j是否有边。1表示有边,\bowtie表示没有边,这里我们将自己到自己(即i等于j)设为0。我们将这种存储图的方法称为图的邻接矩阵存储法。

深度优先搜索的程序实现:

#include <bits/stdc++.h>
using namespace std;
int inf = 99999999;
//构建图
int e[6][6] = {
	{0,   0,   0,   0,   0,   0},
	{0,   0,   1,   1, inf,   1},
	{0,   1,   0, inf,   1, inf},
	{0,   1, inf,   0, inf,   1},
	{0, inf,   1, inf,   0, inf},
	{0,   1, inf,   1, inf,   0}};
//记录访问标识,避免重复访问
int book[101] = {0};
int sum = 0;
int n = 5;
void dfs(int cur) {
	cout << cur << " ";
	sum++;
	if (sum == n) return;
	for (int i = 1; i <= n; i++) {
		if (e[cur][i] == 1 && book[i] == 0) {
			book[i] = 1;
			dfs(i);
		}
	}
	return;
}
int main() {
	book[1]=1;
	dfs(1);
	return 0;
}

 在上面的代码中变量cur存储的是当前正在遍历的顶点,二维数组 e存储的就是图的边(邻接矩阵),数组 book用来记录哪些顶点已经访问过,变量sum用来记录已经访问过多少个顶点,变量n存储的是图的顶点的总个数。

广度优先搜索的程序实现:

#include <bits/stdc++.h>
using namespace std;
int inf = 99999999;
//构建图
int e[6][6] = {
	{0,   0,   0,   0,   0,   0},
	{0,   0,   1,   1, inf,   1},
	{0,   1,   0, inf,   1, inf},
	{0,   1, inf,   0, inf,   1},
	{0, inf,   1, inf,   0, inf},
	{0,   1, inf,   1, inf,   0}
};
//记录访问标识,避免重复访问
int _queue[10];
int head, tail;

int book[101] = {0};
int n = 5;
void bfs() {
	while (head < tail) {
		int cur=_queue[head];
		for (int i = 1; i <= n; i++) {
			if (e[cur][i] == 1 && book[i] == 0) {
				book[i] = 1;
				//将数据加入队列
				_queue[tail] = i;
				tail++;
			}
		}
		head++;
	}
	return;
}
int main() {
	//队列初始化
	head = tail = 1;
	_queue[tail] = 1;
	book[1] = 1;
	tail++;

	bfs();
	
	for(int i=1;i<tail;i++){
		cout<<_queue[i]<<" ";
	}
	return 0;
}

广度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。

2、图的深度遍历——城市地图

题目描述:(带权值的有向图)

试计算从1号城市到5号城市的最短路径。 

实现程序:

#include <bits/stdc++.h>
using namespace std;
int inf = 99999999;
//构建图的邻接矩阵,从1开始编号,将0编号的内容赋值为0
int e[6][6] = {
	{0,   0,   0,   0,   0,    0},
	{0,   0,   2, inf, inf,   10},
	{0, inf,   0,   3, inf,    7},
	{0,   4, inf,   0,   4,  inf},
	{0, inf, inf, inf,   0,    5},
	{0, inf, inf,   3, inf,    0}
};
//记录访问标识,避免重复访问
int book[101] = {0};
//记录走过的距离和最优距离
int sum_dis = 0, min_dis = inf, n = 5;
//记录走过的路径城市与最优路径城市
int _count = 0, _min_count = 0;
int city[20], min_city[20];
//深度优先搜索
void dfs(int cur) {
	if (sum_dis > min_dis)return;
	if (cur == 5) {  //达到目的地后的路径存储和距离存储
		if (min_dis > sum_dis) {
			min_dis = sum_dis;
			_min_count = _count;
			//将路径放入到最优路径内
			for (int i = 1; i <= _count; i++)
				min_city[i] = city[i];
		}
		return;
	}
	//遍历所有的路径点
	for (int i = 1; i <= n; i++) {
		if (e[cur][i] < inf  && book[i] == 0) {
			book[i] = 1;
			sum_dis += e[cur][i];
			city[++_count] = i;
			dfs(i);
			book[i] = 0;
			_count--;
			sum_dis -= e[cur][i];
		}
	}
	return;
}
int main() {
	//初始化第一个点
	_count++;
	city[_count] = 1; //将第一个路径点装入
	book[1] = 1;
	dfs(1);
	//打印输出
	cout << min_dis << endl;
	for (int i = 1; i <= _min_count; i++)
		cout << min_city[i] << " ";
	return 0;
}

3、图的广度变量——最小转机

题目描述:(不带权值的无向图)

 试计算从1号城市到5号城市的最小经过点。 (广度优先搜索适合求权值为1的最优路径求解)

实现程序:

#include <bits/stdc++.h>
using namespace std;
//队列结构体,记录当前的城市和转机的城市数量
struct node {
	int city;
	int count_city;
};

//定义结构体变量
struct node _queue[30];
int head, tail;
//图的节点个数
int n = 5;
//构建图的邻接矩阵
int e[6][6] = {
	{0, 0, 0, 0, 0, 0},
	{0, 0, 1, 1, 0, 0},
	{0, 1, 0, 1, 1, 0},
	{0, 1, 1, 0, 1, 1},
	{0, 0, 1, 1, 0, 1},
	{0, 0, 0, 1, 1, 0},
};
//避免走重复的路径
int book[10] = {0};
//广度优先搜索算法
void bfs() {
	int flag=0;
	while (head < tail) { //队列不为空 
		int cur=_queue[head].city;
		for (int i = 1; i <= n; i++) {
			//如果该点可以拓展,进行队列拓展
			if(e[cur][i]==1 && book[i]==0){ 
				book[i]=1;
				_queue[tail].city=i;
				//此处比较重要,注意数值的递增 
				_queue[tail].count_city=_queue[head].count_city+1;
				tail++;
				if(i==n){
					flag=1;
					break;
				}
			} 	
		}
		if(flag==1)break;
		head++;
	}

}

int main() {
	//初始化队列
	head = tail = 1;
	_queue[tail].city = 1;
	_queue[tail].count_city = 0;
	book[1] = 1;
	tail++;
	//深度优先的搜索 
	bfs();
	//打印搜索结果 
	cout << _queue[tail - 1].count_city << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值