深度优先搜索- 不撞南墙不回头
深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.
下图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜索结束).
简要说明深度优先搜索的特点:每次深度优先搜索的结果必然是图的一个连通分量.深度优先搜索可以从多点发起.如果将每个节点在深度优先搜索过程中的"结束时间"排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的"拓扑排序",即topological sort.
题二、有九张(1-9)的扑克牌,放入九个盒子里面,使得 _ _ _ + _ _ _ = _ _ _ 成立,
现在使用深度优先搜索优化一下之前的方法,之前使用枚举算法,时间复杂度太高
int a[10], book[10], n , total = 0;// C语言全局变量在没有赋值的时候,默认为0
void dfsTwo(int step); // 深度优先搜索
char* getDateTime(void);
int main(int argc, const char * argv[]) {
char * time = getDateTime();
printf("时间 ====%s \n",time);
dfsTwo(1); // 首先在第一个盒子里面
printf("总共有%d种方法",total/2);
printf("时间 ====%s \n",time);
getchar();
}
void dfsTwo(int step) // 深度优先搜索
{
int i = 0;
if (step ==10) { // 如果是第十个个盒子,说明之前的九个盒子里面都是有扑克牌的
// 判断是否满足等式 _ _ _ + _ _ _ = _ _ _
if ((100*a[1] + 10*a[2] + a[3])+(100*a[4] + 10*a[5] + a[6])==(100*a[7] + 10*a[8] + a[9]) ) {
// 如果满足条件, 解加一,并打印这个解
total ++;
printf("%d%d%d+%d%d%d=%d%d%d \n",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);
}
return;
}
// 如果不是第十个盒子,哪应该放那张牌
// 按照 1 - 9 的顺序尝试
for (i = 1; i < 10; i ++) {
// 判断扑克牌是否在手中
if (book[i] == 0) {
// 开始使用手中的扑克牌
a[step] = i;
book[i] = 1;
// 第step个盒子已经放好了扑克牌,走到下面一个盒子里面
dfsTwo(step + 1);
book[i] = 0; // 收回手中的牌,才可以继续操作
}
}
}
char* getDateTime()
{
static char nowtime[20];
time_t rawtime;
struct tm* ltime;
time(&rawtime);
ltime = localtime(&rawtime);
strftime(nowtime, 20, "%Y-%m-%d %H:%M:%S", ltime);
return nowtime;
}
题三、深度优先算法 最短路径
看下列地图,可以的到点a 到点b的最短距离
0 表示空地, 1 表示障碍物 其中 a 和 b 也是在空地里面 ,求a(0,0) 到 b(3,2) 的最短距离 这里坐标为 (行,列)
5行 4列
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
int n3 ,m3 ,p3 ,q3,min3 = 99999999;
int a3[51][51], book3[51][51];
void dfsThree(int x,int y, int step);
char* getDateTime(void);
int main(int argc, const char * argv[]) {
int i , j , startX ,startY;
// 读入n 和 m n为行 m为列
printf("请输入 行 和列:");
scanf("%d %d",&n3, &m3);
// 读入迷宫
for (i = 0; i < n3; i ++ ) {
printf("输入迷宫:\n");
for (j = 0; j < m3; j ++) {
scanf("%d",&a3[i][j]);
}
}
// 读入 起点 和终点
printf("输入起点坐标");
scanf("%d%d",&startX,&startY); // 设置起点坐标为 (startX,startY)
printf("输入终点坐标");
scanf("%d%d",&p3,&q3); // 设置终点坐标为 (p,q)
// 从起点开始搜索
book3[startX][startY] = 1; // 标记起点已经在路径上,防止再重复行走
dfsThree(startX, startY, 0); // 第一个参数 为起点x ,第二个为起点y ,第三个为初始化步数0
printf("输出最短步数 ,%d\n",min3);
getchar();
}
// 得到 A点 到b点的最少步数
void dfsThree(int x,int y, int step) // 深度优先搜索
{
int next [4][2] = {
{0,1}, // 向右走
{1,0}, // 向下走
{0,-1}, // 向左走
{-1,0} // 向上走
};
int tx = 0, ty = 0,k;
// 判断是否达到b点位置
printf("x ==%d y ==%d p3=== %d q3====%d \n",x,y,p3,q3);
if (x == p3 && y == q3) {
// 更新最小步数
if (step < min3) {
printf("输出最短步数 ,%d\n",min3);
min3 = step;
}
return;
}
// 枚举四种走法
for (k = 0; k < 4; k ++) {
// 计算下一个点的坐标
tx = x + next[k][0]; // x = 0 , y = 1 是因为是二维数组 [4][2] {0,1} 其中下标为0 为x 下标为1的为y。 k 数组是为长度为4
ty = y + next[k][1];
// 判断是否越界
if ( tx < 0 || tx > n3 || ty < 0 || ty > m3) {
continue;
}
// 判断改点是否是障碍物或者是已经走过的点
if (a3[tx][ty] == 0 && book3[tx][ty] == 0) { // 0 为空地 1 表示障碍物
// 不是障碍物 或者已经走过的点继续
book3[tx][ty] = 1; //标记这个点已经走过
dfsThree(tx, ty, step + 1);
book3[tx][ty] = 0;
}
}
return;
}
char* getDateTime()
{
static char nowtime[20];
time_t rawtime;
struct tm* ltime;
time(&rawtime);
ltime = localtime(&rawtime);
strftime(nowtime, 20, "%Y-%m-%d %H:%M:%S", ltime);
return nowtime;
}
广度优先搜索 - 层层递进
广度优先搜索算法(Breadth First Search),又称为"宽度优先搜索"或"横向优先搜索",简称BFS。
它的思想是:从图中某顶点v出发,在访问了a之后依次访问a的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。
题三、广度优先算法 最短路径
看下列地图,可以的到点a 到点b的最短距离
0 表示空地, 1 表示障碍物 其中 a 和 b 也是在空地里面 ,求a(0,0) 到 b(3,2) 的最短距离 这里坐标为 (行,列)
5行 4列
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
换句话说,广度优先搜索遍历图的过程是以a为起点,由近至远,依次访问和va路径相通且路径长度为1,2...的顶点。
1、从 (0,0)开始搜索,以a点代替
a 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
-》
2、第二层搜索 从(0,0)搜索,右方和下方 以 b点 代替
a b 1 0
b 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
-》
3、第三层搜索 ,(0,1)这个b点右边是障碍物,无法搜索,可以往下搜索(1,1),(1,0)这个点搜索右方发现已经搜索完成了(每个点需要搜索一次),跳过,搜索(1,0)下方的点 (2,0),以c代替
a b 1 0
b c 0 0
c 0 1 0
0 1 0 0
0 0 0 1
...
就这样一直层层搜索直到搜索完成所有到点(3,2)的路线,结束搜索,遇到障碍物“1”跳过,最后变成下面这个地图
a b 1 f
b c d e
c d 1 f
d 1 h g
e f g 1
完整demo如下
// 广度优化搜索
struct note
{
int x; // 横坐标
int y; // 纵坐标
int f; // 父亲在队列的的编号 ,需要输出路径才需要使用
int s; // 步数
};
char* getDateTime(void);
int main(int argc, const char * argv[]) {
struct note queB[2501]; // 因为地图大小不会超过 50 * 50 所以 队列扩展也不会超过2500个
int aB[51][51], bookB[51][51];
// 定义一个表示方向的数组
int nextB [4][2] = {
{0,1}, // 向右走
{1,0}, // 向下走
{0,-1}, // 向左走
{-1,0} // 向上走
};
int headB ,tailB;
int iB,jB,kB,nB,mB,startX,startY,pB,qB,txB,tyB,flagB;
printf("请输入行和列:");
scanf("%d %d",&nB,&mB);
for (iB = 0; iB < nB; iB ++) {
printf("输入当前行地图");
for (jB = 0; jB < mB; jB++) {
scanf("%d",&aB[iB][jB]);
}
}
printf("输入开始坐标startX,startY 结束坐标 pB qB");
scanf("%d %d %d %d",&startX,&startY,&pB,&qB);
// 队列初始化
headB = 0;
tailB = 0;
// 往队列里面插入迷宫入口坐标
queB[tailB].x = startX;
queB[tailB].y = startY;
queB[tailB].f = 0;
queB[tailB].s = 0;
tailB ++;
bookB[startX][startY] = 1;
flagB = 0;// 标记是否达到目标点,0表示没有达到,1表示已经达到了目标点
// 当队列部位空得时候循环
while (headB < tailB) {
// 枚举4个方向
for (kB = 0; kB< 4; kB ++) {
// 计算下一个点的坐标
txB = queB[headB].x + nextB[kB][0];
tyB = queB[headB].y + nextB[kB][1];
// 判断是否越界
if (txB < 0 || txB > nB || tyB < 0 || tyB > mB ) {
continue;
}
// 判断是否是障碍物,或者是已经走过的路
if (aB[txB][tyB] == 0 && bookB[txB][tyB] == 0) {
// 不是障碍物,并且没有走过的路
// 标记已经走过的点,广度搜索和深度搜索不同,只需要走过一次不需要bookB数组还原
bookB[txB][tyB] = 0;
// 并且把这个点插入到队列里面
queB[tailB].x = txB;
queB[tailB].y = tyB;
queB[tailB].f = headB; // 打印路径需要,因为点事从head里面扩展出来的
queB[tailB].s = queB[headB].s + 1;// 步数是父亲步数的 + 1
tailB ++;
}
// 如果到达目标点,停止扩展,任务结束,退出循环
if (txB == pB && tyB == qB) {
flagB = 1;
break;
}
}
if (flagB == 1) {
break;
}
headB ++; // 当一个点扩展结束之后,需要下一个点开始扩展
}
// 打印队列中末尾最后的一个点的步数
// 注意 tail 是指向队尾 的下一个位置,这里需要减一
// for (iB = 0; iB < tailB; iB ++) {
// printf("%d ",queB[iB].f);
// }
printf("\n最短的步数是:%d\n",queB[tailB - 1].s);
getchar();
return;
}
char* getDateTime()
{
static char nowtime[20];
time_t rawtime;
struct tm* ltime;
time(&rawtime);
ltime = localtime(&rawtime);
strftime(nowtime, 20, "%Y-%m-%d %H:%M:%S", ltime);
return nowtime;
}
本人也是刚刚学习,如有错误,请多多指正。
本文部分内容参考 【啊哈!算法】这本书
代码例子