首页 > 代码库 > 周总结(2017.2.16):第一周算法学习。
周总结(2017.2.16):第一周算法学习。
周总结:算法学习总结之DFS和BFS
一:DFS算法
目的:达到被搜索结构的叶节点。
定义:假定给定图G的初态是所有的定点都没有访问过,在G中任选一定点V为初始出发点,首先访问出发点并标记,然后依次从V出发搜索V的每个相邻点W,若W未曾出现过,则对W进行深度优先遍历(DFS),知道所有和V有路径相通的定点被访问。
如果从V0开始寻找一条长度为4的路径的话:
思路步骤:
先寻找V0的所有相邻点:dis{v1,v2,v3},V1没有访问过,所以对V1进行深度遍历并将V1标记为访问过,此时路径长度为1,然后找到V1的邻点:dis={V2},继续对V2进行深度遍历,此时长度为2,时dis={},发现dis为空,退回V1,发现V1没有其他邻点(因为V2已经被标记过)退回V0,发现邻点就只剩下V3,长度等于2,对V3进行深度遍历并将V3标记,邻点dis = {V4,V5},V4没有访问过,对V4进行深度遍历并将V4标记,长度为3,邻点dis={},退回V3,发现邻点V5,最后找到V6,发现长度=4,满足题意。
自我总结:DFS是一种递归调用,就像你遇见了一个洞,洞里有许多分叉的洞口,你要对你走的每一步预先判定之后的路才能找到最合适的路。
伪代码:
1 bool DFS(int n,int d) 2 if(End(n,d)) //满足结束条件 3 return true; 4 for(Node nextNode int n){ 5 if(!visited[nextNode]){ 6 visited[nextNode] = true; 7 if(DFS(nextNode,n+1)){ 8 //做一些事 9 //return true; 10 } 11 } 12 visited[nextNode] = false; 13 } 14 return false; 15 }
通过本身的递归调用,可以不断的调整深度,知道最后找到想要的深度或者无法找到。对是否访问过做动态的调整,方便找到最后的路径。
例题:六角填数(很简单的DFS例题):题目就不写了
直接上代码:
#include"stdio.h" int a[13] = {0}; int v[13] = {0}; void Jude(); void DFS(int x); void main(){ a[1] = 1;a[2] = 8;a[12] = 3; v[1] = 1;v[8] = 1;v[3] = 1; DFS(1); } void DFS(int x){ int i = 0; if(x==1 || x==2 || x==12){ DFS(x+1); } if(x==13){ Jude(); } for(i=1;i<13;i++){ if(v[i]==0){ v[i] = 1; a[x] = i; DFS(x+1); v[i] = 0; } } } void Jude(){ int i = 0; int b[6] = {0}; b[0] = a[1]+a[3]+a[6]+a[8]; b[1] = a[1]+a[4]+a[7]+a[11]; b[2] = a[2]+a[3]+a[4]+a[5]; b[3] = a[2]+a[6]+a[9]+a[12]; b[4] = a[5]+a[7]+a[10]+a[12]; b[5] = a[8]+a[9]+a[10]+a[11]; for(i=1;i<6;i++){ if(b[i] != b[i-1]){ return ; } } printf("a[6] = %d",a[6]); }
(测试了,代码没有任何问题。)
二:BFS算法
宽度优先算法属于盲目搜索法,是从根节点开始,沿着树的宽度遍历树的节点,如果所有的节点都被访问,则算法终止。
流程:设置起始位置节点为V0,末尾节点为Vd,将V0放入灰色集合体中(将要访问的节点),从灰色集合体中取出元素Vn,标记为黑色,然后观察Vn的所有临界点放入集合Neighbor集合中,观察Neighbor中有没有Vd,如果没有就将Neighbor中元素放入灰色集合体中。直到找到Vd或者所有节点被遍历。
参考维基百科的图片:
图片看起来更加的形象化,a为起始点h(图片可能出了点错)末尾点。
伪代码(直接拿了维基百科的):
/** * ADDQ (Q, p) - p PUSH 入 Q * DELQ (Q) - POP Q 並返回 Q 頂 * FIRSTADJ (G,v) - v 的第一個鄰接點,找不到則返回 -1 * NEXTADJ (G,v) - v 的下一個鄰接點,找不到則返回 -1 * VISIT (v) - 訪問 v * visited [] - 是否已訪問 */ /* 廣度优先搜索算法 */ void BFS(VLink G[], int v) { int w; /* 訪問 v 並入隊 */ VISIT(v); visited[v]=1; ADDQ(Q,v); /* 對隊列 Q 的各元素 */ while(!EMPTYQ(Q)) { v=DELQ(Q); w=FIRSTADJ(G,v); /* 的各鄰接點 */ do { /* 進行訪問和入隊 */ if(visited[w] == 0) { VISIT(w); ADDQ(Q,w); visited[w]=1; } } while (w=NEXTADJ(G,v)) != -1) } } /* 對圖G=(V,E)進行廣度優先搜索的主算法 */ void TRAVEL_BFS(VLink G[], bool visited[], int n) { int i; // 清零標記數組 for(i = 0; i < n; i ++) visited[i] = 0; for(i = 0; i < n; i ++) if(visited[i] == 0) BFS(G,i); }
代码看起来有点不好理解, 因为用了栈的概念,而在c语言中,可以讲栈用数组进行一个代替。
总结:BFS是一种盲目的搜索法,是通过遍历所有的邻节点的邻接点来找到你想要的结果。
例题:走迷宫:
#include "stdio.h" #include "stdlib.h" int dir[4][2]={1,0, //x+1,y -1,0, //x-1,y 0,1, //x,y+1 0,-1}; //x,y-1 //可走的四个方向 struct node{ int x; inty; }; struct node queue[50]; //queue记录可走的点 struct node record[5][5];//record记录改点的前驱 void bfs() { int head,tail,i; struct node cur; struct node next;//cur为当前位置,next为下一个位置 head=tail=0; cur.x=queue[tail].x; cur.y=queue[tail].y; tail++; while(head<tail) { cur=queue[head++]; for(i=0;i<4;i++) { next.x=cur.x+dir[i][0]; next.y=cur.y+dir[i][1]; if(next.x>=0&&next.y>=0&&next.x<5&&next.y<5&&map[next.x][next.y]==0) //0为可走路线,不能跑出地图范围 { record[next.x][next.y].x=cur.x; record[next.x][next.y].y=cur.y;//记录next的前驱,即next的坐标(因为next记录的是第一个到达该地点的前驱,随后被标记走过,故不用担心被后来的前驱坐标所覆盖) if(next.x==4&&next.y==4) return ; else { map[next.x][next.y]=1;//标记走过 queue[tail++]=next; } } } } } int main() { int i,j,k,m,n; struct node cur; for(i=0;i<5;i++) for(j=0;j<5;j++) scanf("%d",&map[i][j]); cur.x=0; cur.y=0; map[0][0]=1; queue[0]=cur; bfs(); k=0; queue[k].x=4; queue[k++].y=4; i=j=4; while(i!=0||j!=0)//根据record的记录,从后往前回溯其路径,并存在queue中 { m=i;n=j; i=record[m][n].x; j=record[m][n].y; queue[k].x=i; queue[k++].y=j; } for(i=k-1;i>=0;i--)//输出路径 printf("(%d, %d)\n",queue[i].x,queue[i].y); return 0; }
代码取自:http://blog.sina.com.cn/s/blog_7e5541250100ssue.html
思路很清晰,注释也很清楚。可以参考下那篇博客(膜拜大神)。
希望各位能够留下自己的想法,我们可以一起交流。我是菜鸟,但我相信,我会变强的。
FIGHTING..............
周总结(2017.2.16):第一周算法学习。