圖遍歷算法——DFS、BFS、A*、B*和Flood Fill 遍歷算法大串講

文章出自:http://dsqiu.iteye.com/blog/1689130

圖遍歷算法——DFS、BFS、A*、B*和Flood Fill 遍歷算法大串講

本文內容框架:

§1 圖遍歷DFS和BFS兩種實現

§2 A*算法

§3 B*算法

§4 Flood Fill算法

§5 小結

圖遍歷問題分爲四類:

遍歷完所有的邊而不能有重複,即所謂“一筆畫問題”或“歐拉路徑”;

遍歷完所有的頂點而沒有重複,即所謂“哈密爾頓問題”。

遍歷完所有的邊而可以有重複,即所謂“中國郵遞員問題”;

遍歷完所有的頂點而可以重複,即所謂“旅行推銷員問題”。

 

對於第一和第三類問題已經得到了完滿的解決,而第二和第四類問題則只得到了部分解決。

第一類問題就是研究所謂的歐拉圖的性質,而第二類問題則是研究所謂的哈密爾頓圖的性質。

 

 

 

§1 圖遍歷DFS和BFS兩種實現

 

圖遍歷的矩陣存儲和鄰接表存儲的BFS和DFS實現

矩陣存儲BFS

 

C代碼  收藏代碼
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <queue>  
  4. using namespace std;  
  5. queue <int> qu, qq;  
  6. int map[100][100], m, n, judge[100], path[100];  
  7. void bfs(){//和導論上的染色的方法差不多,judge[0] == 0 時候代表白色節點   在隊列裏面的節點是灰色的 出隊列的是黑色的。  
  8.     int w, s,t,i, j;  
  9.     while(true){  
  10.         s = qu.size();  
  11.         if(!s)return;  
  12.         while(s--){  
  13.             t = qu.front();  
  14.             for(i = 0; i < n; i++){  
  15.                 if(map[t][i] && !judge[i]){  
  16.                     judge[i] = true;  
  17.                     qu.push(i);  
  18.                     path[i] = t;//記錄寬度優先搜索樹中的當前節點的父親  
  19.                     //printf("path[%d] = %d\n", i, t);  
  20.                 }  
  21.             }  
  22.             qu.pop();  
  23.         }  
  24.     }  
  25. }  
  26. void printpath(int n){ //遞歸的輸出路徑  
  27.     if(path[n] == -1)printf("%d ", n);  
  28.     else{  
  29.         printpath(path[n]);  
  30.         printf("%d ", n);  
  31.     }  
  32. }  
  33. int main(){  
  34.     freopen("bfs.in""r", stdin);  
  35.     freopen("bfs.out""w", stdout);  
  36.     int i, j, u, v;  
  37.     while(scanf("%d%d", &n, &m) != -1){  
  38.         memset(judge, 0, sizeof(judge));  
  39.         for(i = 0; i < m; i++){  
  40.             scanf("%d%d", &u, &v);  
  41.             map[u][v] = map[v][u] = 1;  
  42.         }  
  43.         judge[0] = true;qu = qq;  
  44.         qu.push(0);memset(path, -1, sizeof(path));  
  45.         bfs();  
  46.         for(i = 1; i < n; i++){  
  47.             printf("from 0 to %d : ", i);  
  48.             printpath(i);puts("");  
  49.         }  
  50.     }  
  51.     return 0;  
  52. }  
 

鏈表存儲BFS

 

C代碼  收藏代碼
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <queue>  
  4. using namespace std;  
  5. queue<int> qu, qq;  
  6. struct e{  
  7.     int v;  
  8.     e* next;  
  9. };  
  10. e* edge[100];int m, n, judge[100], path[100];  
  11. void bfs(){  
  12.     int w, i, j, t, s;e* l;  
  13.     while(true){  
  14.         s = qu.size();  
  15.         if(!s)return;  
  16.         while(s--){  
  17.             w = qu.front();  
  18.             l = edge[w];  
  19.             while(l){  
  20.                 t = l->v;  
  21.                 if(!judge[t]){  
  22.                     judge[t] = true;  
  23.                     qu.push(t);  
  24.                     path[t] = w;  
  25.                 }  
  26.                 l = l->next;  
  27.             }  
  28.             qu.pop();  
  29.         }  
  30.     }  
  31. }  
  32. void printpath(int x){  
  33.     if(path[x] == -1)printf("%d ", x);  
  34.     else{  
  35.         printpath(path[x]);  
  36.         printf(" %d", x);  
  37.     }  
  38. }  
  39. int main(){  //個人不推薦動態開闢存儲空間,建議靜態。  
  40.     freopen("bfs_link.in""r", stdin);  
  41.     freopen("bfs_link.out""w", stdout);  
  42.     int u, v, i, j;e* node;  
  43.     while(scanf("%d%d", &n, &m) != -1){  
  44.         memset(judge, 0, sizeof(judge));  
  45.         memset(path, -1, sizeof(path));  
  46.         for(i = 0; i < m; i++){  
  47.             scanf("%d%d", &u, &v);  
  48.             node = new e;  
  49.             node->v = v;  
  50.             node->next = edge[u];  
  51.             edge[u] = node;  
  52.             node = new e;  
  53.             node->v = u;  
  54.             node->next = edge[v];  
  55.             edge[v] = node;  
  56.         }  
  57.         judge[0] = true;qu = qq;qu.push(0);  
  58.         bfs();  
  59.         for(i = 1; i < n; i++){  
  60.             printf("path from 0 to %d : ", i);  
  61.             printpath(i);puts("");  
  62.         }  
  63.     }  
  64.     return 0;  
  65. }  
 

矩陣存儲DFS

 

C代碼  收藏代碼
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. int map[100][100], m, n, d[100], f[100], time, path[100];  
  4. bool judge[100];  
  5. void dfs(int v){  
  6.     int i;judge[v] = true;  
  7.     time++;d[v] = time;//開始時間  
  8.     for(i = 0; i < n; i++){  
  9.         if(map[v][i] && !judge[i]){  
  10.             path[i] = v;//記錄深度優先搜索樹中的父親節點  
  11.             dfs(i);  
  12.         }  
  13.     }  
  14.     time++;f[v] = time;//結束時間  
  15. }  
  16.   
  17. void printpath(int v){  
  18.     if(path[v] == -1)printf("%d ", v);  
  19.     else{  
  20.         printpath(path[v]);  
  21.         printf(" %d", v);  
  22.     }  
  23. }  
  24.   
  25. int main(){  
  26.     freopen("dfs_m.in""r", stdin);  
  27.     freopen("dfs_m.out""w", stdout);  
  28.     int i, j, u, v;  
  29.     while(scanf("%d%d", &n, &m) != -1){  
  30.         memset(map, 0, sizeof(map));  
  31.         memset(judge, 0, sizeof(judge));  
  32.         memset(path, -1, sizeof(path));  
  33.         for(i = 0; i < m; i++){  
  34.             scanf("%d%d", &u, &v);  
  35.             map[u][v] = map[v][u] = true;  
  36.         }  
  37.         time = 0;dfs(0);  
  38.         for(i = 0; i < n; i++){  
  39.             printf("d[%d] = %d   f[%d] = %d\n", i, d[i], i, f[i]);  
  40.         }  
  41.         for(i = 1; i < n; i++){  
  42.             printf("path from 0 to %d : ");  
  43.             printpath(i);puts("");  
  44.         }  
  45.     }  
  46.     return 0;  
  47. }  
 

鏈表存儲BFS

 

C代碼  收藏代碼
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. struct e{  
  4.     int v;  
  5.     e* next;  
  6. };  
  7. e* link[100];e edge[10000];//靜態的  
  8. int m, n, el, judge[100], d[100], f[100], time, path[100];  
  9. void dfs(int v){  
  10.     int i, t;e* l;  
  11.     judge[v] = true;  
  12.     time++;d[v] = time;  
  13.     l = link[v];  
  14.     while(l){  
  15.         t = l->v;  
  16.         if(!judge[t]){  
  17.             judge[t] = true;  
  18.             path[t] = v;  
  19.             dfs(t);  
  20.         }  
  21.         l = l->next;  
  22.     }  
  23.     time++;f[v] = time;  
  24. }  
  25.   
  26. void printpath(int v){  
  27.     if(path[v] == -1)printf("%d", v);  
  28.     else{  
  29.         printpath(path[v]);  
  30.         printf(" %d", v);  
  31.     }  
  32. }  
  33.   
  34. int main(){  
  35.     freopen("dfs_link.in""r", stdin);  
  36.     freopen("dfs_link.out""w", stdout);  
  37.     int i, j, u, v;  
  38.     while(scanf("%d%d", &n, &m) != -1){  
  39.         memset(judge, 0, sizeof(judge));  
  40.         memset(link, 0, sizeof(edge));  
  41.         memset(path, -1, sizeof(path));  
  42.         for(i = 0, el = 0; i < m; i++){  
  43.             scanf("%d%d", &u, &v);  
  44.             edge[el].v = v;  
  45.             edge[el].next = link[u];  
  46.             link[u] = &edge[el++];  
  47.             edge[el].v = u;  
  48.             edge[el].next = link[v];  
  49.             link[v] = &edge[el++];  
  50.         }time = 0;  
  51.         for(i = 0; i < n; i++){  
  52.             if(!judge[i])dfs(i);  
  53.         }  
  54.         for(i = 0; i < n; i++){  
  55.             printf("d[%d] = %d  f[%d] = %d\n", i, d[i], i, f[i]);  
  56.         }  
  57.         for(i = 1; i < n; i++){  
  58.             printf("path form 0 to %d : ", i);  
  59.             printpath(i);puts("");  
  60.         }  
  61.     }  
  62.     return 0;  
  63. }  

  ╝①

 

§2 A*算法

 

A*算法

 

A*算法是一種常用的啓發式搜索算法。


在A*算法中,一個結點位置的好壞用估價函數來對它進行評估。A*算法的估價函數可表示爲: 
f'(n) = g'(n) + h'(n) 
這裏,f'(n)是估價函數,g'(n)是起點到終點的最短路徑值(也稱爲最小耗費或最小代價),h'(n)是n到目標的最短路經的啓發值。由於這個f'(n)其實是無法預先知道的,所以實際上使用的是下面的估價函數:
f(n) = g(n) + h(n) 
其中g(n)是從初始結點到節點n的實際代價,h(n)是從結點n到目標結點的最佳路徑的估計代價。在這裏主要是h(n)體現了搜索的啓發信息,因爲g(n)是已知的。用f(n)作爲f'(n)的近似,也就是用g(n)代替g'(n),h(n)代替h'(n)。這樣必須滿足兩個條件:(1)g(n)>=g'(n)(大多數情況下都是滿足的,可以不用考慮),且f必須保持單調遞增。(2)h必須小於等於實際的從當前節點到達目標節點的最小耗費h(n)<=h'(n)。第二點特別的重要。可以證明應用這樣的估價函數是可以找到最短路徑的。


A*算法的具體步驟
A*算法基本上與廣度優先算法相同,但是在擴展出一個結點後,要計算它的估價函數,並根據估價函數對待擴展的結點排序,從而保證每次擴展的結點都是估價函數最小的結點。
1)建立一個隊列,計算初始結點的估價函數f,並將初始結點入隊,設置隊列頭和尾指針。
2)取出隊列頭(隊列頭指針所指)的結點,如果該結點是目標結點,則輸出路徑,程序結束。否則對結點進行擴展。 
3)檢查擴展出的新結點是否與隊列中的結點重複,若與不能再擴展的結點重複(位於隊列頭指針之前),則將它拋棄;若新結點與待擴展的結點重複(位於隊列頭指針之後),則比較兩個結點的估價函數中g的大小,保留較小g值的結點。跳至第五步。
4)如果擴展出的新結點與隊列中的結點不重複,則按照它的估價函數f大小將它插入隊列中的頭結點後待擴展結點的適當位置,使它們按從小到大的順序排列,最後更新隊列尾指針。
5)如果隊列頭的結點還可以擴展,直接返回第二步。否則將隊列頭指針指向下一結點,再返回第二步。

╝②

 

A*算法圖解

 

 如圖所示簡易地圖, 其中綠色方塊的是起點 (用 A 表示), 中間藍色的是障礙物, 紅色的方塊 (用 B 表示) 是目的地. 爲了可以用一個二維數組來表示地圖, 我們將地圖劃分成一個個的小方塊。

 

 

 

二維數組在遊戲中的應用是很多的, 比如貪吃蛇和俄羅斯方塊基本原理就是移動方塊而已. 而大型遊戲的地圖, 則是將各種"地貌"鋪在這樣的小方塊上。

 

 


F = G + H,               

G 表示從起點 A 移動到網格上指定方格的移動耗費 (可沿斜方向移動)。 

H 表示從指定的方格移動到終點 B 的預計耗費 (H 有很多計算方法, 這裏我們設定只可以上下左右移動)。

   我們假設橫向移動一個格子的耗費爲10, 爲了便於計算, 沿斜方向移動一個格子耗費是14. 爲了更直觀的展示如何運算 F,G,H, 圖中方塊的左上角數字表示 F, 左下角表示 G, 右下角表示 H。 看看上圖是否跟你心裏想的結果一樣?

 將上圖A周圍的方塊全部放入隊列,取出F值最小的方塊C,看看 C 下面的那個格子, 它目前的 G 是14, 如果通過 C 到達它的話, G將會是 10 + 10, 這比 14 要大, 因此我們什麼也不做(上面步驟3))。

以C爲中心結點,擴展新結點 ,並進入隊列(上面步驟4))。

直到最終找到終點B,上面淺藍色邊緣的方塊是移動過的地點(實際走過的地方),淺綠色邊緣的方塊是還在隊列中的方塊。

╝③  

A*算法實現

 

C代碼  收藏代碼
  1. /* 
  2.  * file: astar_algorithm.h 
  3.  * author: MulinB@HUST 
  4.  * date: 2010-10-10 
  5.  * modified: 2012-05-09 
  6.  * A-star algorithm implemented in C. Only for study. 
  7.  */  
  8. #ifndef _ASTAR_ALGORITHM_H  
  9. #define _ASTAR_ALGORITHM_H  
  10. #include <math.h>  
  11. #define M  6  
  12. #define N  8  
  13. //map marks  
  14. #define  AVAIL     0  
  15. #define  UNAVAIL  -1  
  16. #define  START   100  
  17. #define  END     111  
  18. #define  ROAD     10  
  19. #define GET_F(X)  (X->G + X->H)  
  20. typedef struct Node  
  21. {  
  22.     //for node itself  
  23.     int type; //node type  
  24.     int i; //i index  
  25.     int j; //j index  
  26.     //for A star algorithm  
  27.     double G; //past road cost  
  28.     double H; //heuristic, F = G + H  
  29.     struct Node* parent; //parent node, used for trace road  
  30.     struct Node* next; //only used for open and close list  
  31. }Node;  
  32.   
  33. //==========================open close list operation================  
  34. Node* open_list;  
  35. Node* close_list;  
  36. void init_openlist()  
  37. {  
  38.     open_list = NULL;  
  39. }  
  40. void init_closelist()  
  41. {  
  42.     close_list = NULL;  
  43. }  
  44. void destroy_openlist()  
  45. {  
  46.     Node* q;  
  47.     Node* p = open_list;  
  48.     while (p != NULL)  
  49.     {  
  50.         q = p->next;  
  51.         free(p);  
  52.         p = q;  
  53.     }  
  54. }  
  55. void destroy_closelist()  
  56. {  
  57.     Node* q;  
  58.     Node* p = close_list;  
  59.     while (p != NULL)  
  60.     {  
  61.         q = p->next;  
  62.         free(p);  
  63.         p = q;  
  64.     }  
  65. }  
  66. void insert_into_openlist(Node* new_node) //insert and sort by F  
  67. {  
  68.     Node* p;  
  69.     Node* q;  
  70.     if (open_list == NULL)  
  71.     {  
  72.         open_list = new_node; //insert as the first  
  73.         return;  
  74.     }  
  75.     p = open_list;  
  76.     while (p != NULL)  
  77.     {  
  78.         q = p;  
  79.         p = p->next;  
  80.         if (p == NULL)  
  81.         {  
  82.             q->next = new_node; //insert as the last  
  83.             return;  
  84.         }  
  85.         else if (GET_F(new_node) < GET_F(p))  
  86.         {  
  87.             q->next = new_node; //insert before p, sorted  
  88.             new_node->next = p;  
  89.             return;  
  90.         }  
  91.     }  
  92.       
  93. }  
  94. void insert_into_closelist(Node* new_node) //just insert before head  
  95. {  
  96.     if (close_list == NULL)  
  97.     {  
  98.         close_list = new_node; //insert as the first  
  99.         return;  
  100.     }  
  101.     else  
  102.     {  
  103.         new_node->next = close_list; //insert before head  
  104.         close_list = new_node;  
  105.         return;  
  106.     }  
  107. }  
  108. Node* find_node_in_list_by_ij(Node* node_list, int di, int dj)  
  109. {  
  110.     Node* p = node_list;  
  111.     while (p)  
  112.     {  
  113.         if (p->i == di && p->j == dj)  
  114.             return p;  
  115.         p = p->next;  
  116.     }  
  117.     return NULL;  
  118. }  
  119. Node* pop_firstnode_from_openlist() //get the minimum node sorted by F  
  120. {  
  121.     Node* p = open_list;  
  122.     if (p == NULL)  
  123.     {  
  124.         return NULL;  
  125.     }  
  126.     else  
  127.     {  
  128.         open_list = p->next;  
  129.         p->next = NULL;  
  130.         return p;  
  131.     }  
  132. }  
  133. void remove_node_from_openlist(Node* nd) //just remove it, do not destroy it  
  134. {  
  135.     Node* q;  
  136.     Node* p = open_list;  
  137.     if (open_list == nd)  
  138.     {  
  139.         open_list = open_list->next;  
  140.         return;  
  141.     }  
  142.     while (p)  
  143.     {  
  144.         q = p;  
  145.         p = p->next;  
  146.         if (p == nd) //found  
  147.         {  
  148.             q->next = p->next;  
  149.             p->next = NULL;  
  150.             return;  
  151.         }  
  152.     }  
  153. }  
  154. void remove_node_from_closelist(Node* nd) //just remove it, do not destroy it  
  155. {  
  156.     Node* q;  
  157.     Node* p = close_list;  
  158.     if (close_list == nd)  
  159.     {  
  160.         close_list = close_list->next;  
  161.         return;  
  162.     }  
  163.     while (p)  
  164.     {  
  165.         q = p;  
  166.         p = p->next;  
  167.         if (p == nd) //found  
  168.         {  
  169.             q->next = p->next;  
  170.             p->next = NULL;  
  171.             return;  
  172.         }  
  173.     }  
  174. }  
  175. //===================================================================  
  176. //=======================calculate H, G =============================  
  177. //calculate Heuristic value  
  178. //(reimplemented when porting a star to another application)  
  179. double calc_H(int cur_i, int cur_j, int end_i, int end_j)  
  180. {  
  181.     return (abs(end_j - cur_j) + abs(end_i - cur_i)) * 10.0; //the heuristic  
  182. }  
  183. //calculate G value  
  184. //(reimplemented when porting a star to another application)  
  185. double calc_G(Node* cur_node)  
  186. {  
  187.     Node* p = cur_node->parent;  
  188.     if (abs(p->i - cur_node->i) + abs(p->j - cur_node->j) > 1)  
  189.         return 14.0 + p->G; //the diagonal cost is 14  
  190.     else  
  191.         return 10.0 + p->G; //the adjacent cost is 10  
  192. }  
  193. void init_start_node(Node* st, int si, int sj, int ei, int ej)  
  194. {  
  195.     memset(st, 0, sizeof(Node));  
  196.     st->type = START;  
  197.     st->i = si;  
  198.     st->j = sj;  
  199.     st->H = calc_H(si, sj, ei, ej);  
  200.     st->G = 0;  
  201. }  
  202. void init_end_node(Node* ed, int ei, int ej)  
  203. {  
  204.     memset(ed, 0, sizeof(Node));  
  205.     ed->type = END;  
  206.     ed->i = ei;  
  207.     ed->j = ej;  
  208.     ed->H = 0;  
  209.     ed->G = 9999; //temp value  
  210. }  
  211. void init_pass_node(Node* pd, int pi, int pj)  
  212. {  
  213.     memset(pd, 0, sizeof(Node));  
  214.     pd->type = AVAIL;  
  215.     pd->i = pi;  
  216.     pd->j = pj;  
  217. }  
  218. //check the candidate node (i,j) when extending parent_node  
  219. int check_neighbor(int map[][N], int width, int height,   
  220.     int di, int dj, Node* parent_node, Node* end_node)  
  221. {  
  222.     Node* p;  
  223.     Node* temp;  
  224.     double new_G;  
  225.     if (di < 0 || dj < 0 || di > height-1 || dj > width-1)  
  226.         return UNAVAIL;  
  227.     //1. check available  
  228.     if (map[di][dj] == UNAVAIL)  
  229.         return UNAVAIL;  
  230.     //2. check if existed in close list  
  231.     p = find_node_in_list_by_ij(close_list, di, dj);   
  232.     if (p != NULL)  
  233.     {  
  234.         //found in the closed list, check if the new G is better, added 2012-05-09  
  235.         temp = p->parent;  
  236.         p->parent = parent_node;  
  237.         new_G = calc_G(p);  
  238.         if (new_G >= p->G)  
  239.         {  
  240.             p->parent = temp; //if new_G is worse, recover the parent  
  241.         }  
  242.         else  
  243.         {  
  244.             //the new_G is better, remove it from close list, insert it into open list  
  245.             p->G = new_G;  
  246.             remove_node_from_closelist(p); //remove it  
  247.             insert_into_openlist(p); //insert it, sorted  
  248.         }  
  249.         return AVAIL;  
  250.     }  
  251.     //3. check if existed in open list  
  252.     p = find_node_in_list_by_ij(open_list, di, dj); //in open list  
  253.     if (p != NULL)  
  254.     {  
  255.         //found in the open list, check if the new G is better  
  256.         temp = p->parent;  
  257.         p->parent = parent_node;  
  258.         new_G = calc_G(p);  
  259.         if (new_G >= p->G)  
  260.         {  
  261.             p->parent = temp; //if new_G is worse, recover the parent  
  262.         }  
  263.         else  
  264.         {  
  265.             //the new_G is better, resort the list  
  266.             p->G = new_G;  
  267.             remove_node_from_openlist(p); //remove it  
  268.             insert_into_openlist(p); //insert it, sorted  
  269.         }  
  270.         return AVAIL;  
  271.     }  
  272.       
  273.     //4. none of above, insert a new node into open list  
  274.     if (map[di][dj] == END)  
  275.     {  
  276.         //4~. check if it is end node  
  277.         end_node->parent = parent_node;  
  278.         end_node->G = calc_G(end_node);  
  279.         insert_into_openlist(end_node); //insert into openlist  
  280.         return AVAIL;  
  281.     }  
  282.     else  
  283.     {  
  284.         //4~~. create a new node  
  285.         p = malloc(sizeof(Node));  
  286.         init_pass_node(p, di, dj);  
  287.         p->parent = parent_node;  
  288.         p->H = calc_H(di, dj, end_node->i, end_node->j);  
  289.         p->G = calc_G(p);  
  290.         insert_into_openlist(p); //insert into openlist  
  291.         return AVAIL;  
  292.     }  
  293. }  
  294. //extend the current node on the map  
  295. //(reimplemented when porting a star to another application)  
  296. void extend_node(Node* cd, int map[][N], int width, int height, Node* end_node)  
  297. {  
  298.     int up_status, down_status, left_status, right_status;  
  299.     int ci, cj; //cur node i, j  
  300.     int ti, tj; //temp i, j  
  301.     ci = cd->i;  
  302.     cj = cd->j;  
  303.     //1. up  
  304.     ti = ci - 1;  
  305.     tj = cj;  
  306.     up_status = check_neighbor(map, width, height, ti, tj, cd, end_node);  
  307.     //2. down  
  308.     ti = ci + 1;  
  309.     tj = cj;  
  310.     down_status = check_neighbor(map, width, height, ti, tj, cd, end_node);  
  311.     //3. left  
  312.     ti = ci;  
  313.     tj = cj - 1;  
  314.     left_status = check_neighbor(map, width, height, ti, tj, cd, end_node);  
  315.     //4. right  
  316.     ti = ci;  
  317.     tj = cj + 1;  
  318.     right_status = check_neighbor(map, width, height, ti, tj, cd, end_node);  
  319.     //5. leftup  
  320.     ti = ci - 1;  
  321.     tj = cj - 1;  
  322.     if (up_status == AVAIL && left_status == AVAIL)  
  323.         check_neighbor(map, width, height, ti, tj, cd, end_node);  
  324.     //6. rightup  
  325.     ti = ci - 1;  
  326.     tj = cj + 1;  
  327.     if (up_status == AVAIL && right_status == AVAIL)  
  328.         check_neighbor(map, width, height, ti, tj, cd, end_node);  
  329.     //7. leftdown  
  330.     ti = ci + 1;  
  331.     tj = cj - 1;  
  332.     if (down_status == AVAIL && left_status == AVAIL)  
  333.         check_neighbor(map, width, height, ti, tj, cd, end_node);  
  334.     //8. rightdown  
  335.     ti = ci + 1;  
  336.     tj = cj + 1;  
  337.     if (down_status == AVAIL && right_status == AVAIL)  
  338.         check_neighbor(map, width, height, ti, tj, cd, end_node);  
  339.       
  340. }  
  341.   
  342. //=======================search algorithm======================================  
  343. /* 
  344. A*方法總結 (from http://www.policyalmanac.org/games/aStarTutorial.htm): 
  345. 1. 把起始格添加到開啓列表。 
  346. 2. 重複如下的工作: 
  347.   a) 尋找開啓列表中F值最低的格子。我們稱它爲當前格。 
  348.   b) 把它切換到關閉列表。 
  349.   c) 對相鄰的8格中的每一個? 
  350.     * 如果它不可通過或者已經在關閉列表中,略過它。反之如下。(MulinB 2012-05-09 按:在關閉列表中是否也應該檢查它,看是否可以獲得更低的G值?? ref: http://theory.stanford.edu/~amitp/GameProgramming/ImplementationNotes.html ) 
  351.     * 如果它不在開啓列表中,把它添加進去。把當前格作爲這一格的父節點。記錄這一格的F,G,和H值。 
  352.     * 如果它已經在開啓列表中,用G值爲參考檢查新的路徑是否更好。更低的G值意味着更好的路徑。 
  353.       如果是這樣,就把這一格的父節點改成當前格,並且重新計算這一格的G和F值。 
  354.       如果你保持你的開啓列表按F值排序,改變之後你可能需要重新對開啓列表排序。 
  355.   d) 停止,當你 
  356.     * 把目標格添加進了關閉列表(註解),這時候路徑被找到,或者 
  357.     * 沒有找到目標格,開啓列表已經空了。這時候,路徑不存在。 
  358. 3. 保存路徑。從目標格開始,沿着每一格的父節點移動直到回到起始格。這就是你的路徑。 
  359. */  
  360. //search a road on a map, return node_list  
  361. Node* a_star_search(int map[M][N], int width, int height,   
  362.                     int start_i, int start_j, int end_i, int end_j)  
  363. {  
  364.     Node* cur_node;  
  365.     Node* start_node;  
  366.     Node* end_node;  
  367.     //create start and end node  
  368.     start_node = malloc(sizeof(Node));  
  369.     init_start_node(start_node, start_i, start_j, end_i, end_j);  
  370.     end_node = malloc(sizeof(Node));  
  371.     init_end_node(end_node, end_i, end_j);  
  372.       
  373.     //init open and close list  
  374.     init_openlist();  
  375.     init_closelist();  
  376.     //put start_node into open list  
  377.     insert_into_openlist(start_node);  
  378.       
  379.     //start searching  
  380.     while (1)  
  381.     {  
  382.         cur_node = pop_firstnode_from_openlist(); //it has the minimum F value  
  383.         if (cur_node == NULL || cur_node->type == END)  
  384.         {  
  385.             break//found the road or no road found  
  386.         }  
  387.           
  388.         extend_node(cur_node, map, width, height, end_node); //the key step!!  
  389.         insert_into_closelist(cur_node);  
  390.     }  
  391.     //you can track the road by the node->parent  
  392.     return cur_node;  
  393. }  
  394.   
  395. #endif /* file end */  

 ╝④

 

 

B*算法

B* 尋路算法又叫Branch Star 分支尋路算法,且與A*對應,本算法適用於遊戲中怪物的自動尋路,其效率遠遠超過A*算法,經過測試,效率是普通A*算法的幾十上百倍。 
通過引入該算法,一定程度上解決了遊戲服務器端無法進行常規尋路的效率問題,除非服務器端有獨立的AI處理線程,否則在服務器端無法允許可能消耗大量時間的尋路搜索,即使是業界普遍公認的最佳的A*,所以普遍的折中做法是服務器端只做近距離的尋路,或通過導航站點縮短A*的範圍。 

 

 

§3 B*算法

B*算法原理 
本算法啓發於自然界中真實動物的尋路過程,並加以改善以解決各種阻擋問題(有點類似蟻羣算法)。 
前置定義: 
1、探索節點: 
爲了敘述方便,我們定義在尋路過程中向前探索的節點(地圖格子)稱爲探索節點,起始探索節點即爲原點。(探索節點可以對應爲A*中的開放節點)。
2、自由的探索節點: 
探索節點朝着目標前進,如果前方不是阻擋,探索節點可以繼續向前進入下一個地圖格子,這種探索節點我們稱爲自由探索節點; 
3、繞爬的探索節點: 
探索節點朝着目標前進,如果前方是阻擋,探索節點將試圖繞過阻擋,繞行中的探索節點我們成爲繞爬的探索節點; 
B*算法流程 
1、起始,探索節點爲自由節點,從原點出發,向目標前進; 
2、自由節點前進過程中判斷前面是否爲障礙, 
      a、不是障礙,向目標前進一步,仍爲自由節點; 
      b、是障礙,以前方障礙爲界,分出左右兩個分支,分別試圖繞過障礙,這兩個分支節點即成爲兩個繞爬的探索節點; 
3、繞爬的探索節點繞過障礙後,又成爲自由節點,回到2); 
4、探索節點前進後,判斷當前地圖格子是否爲目標格子,如果是則尋路成功,根據尋路過程構造完整路徑; 
5、尋路過程中,如果探索節點沒有了,則尋路結束,表明沒有目標格子不可達; 

B*算法圖解演示 
     
    


B*算法與A*算法的性能比較 
尋路次數比較(5秒鐘尋路次數) 

 

╝⑤

 

§4 Flood Fill算法

 

Flood Fill算法

 

Flood Fill算法是計算機圖形學和數字圖像處理的一個填充算法,其實就是從一點開始向四面周圍尋找點填充遍歷,原理和BFS很相似,當然也可以像DFS一樣的遍歷。

 

Flood Fill 算法圖解演示


Flood Fill算法實現

說的Flood Fill算法的實現不得不提Lode's Computer Graphics Tutorial。該算法可以通過遞歸或者是stack來完成,下面只附上4-Way Recurisive Method:

 

Cpp代碼  收藏代碼
  1. //Recursive 4-way floodfill, crashes if recursion stack is full   
  2. void floodFill4(int x, int y, int newColor, int oldColor)   
  3. {   
  4.     if(x >= 0 && x < w && y >= 0 && y < h && screenBuffer[x][y] == oldColor && screenBuffer[x][y] != newColor)   
  5.     {   
  6.         screenBuffer[x][y] = newColor; //set color before starting recursion  
  7.           
  8.         floodFill4(x + 1, y,     newColor, oldColor);  
  9.         floodFill4(x - 1, y,     newColor, oldColor);  
  10.         floodFill4(x,     y + 1, newColor, oldColor);  
  11.         floodFill4(x,     y - 1, newColor, oldColor);  
  12.     }       
  13. }  
 

 

 

 

§5 小結

這篇文章摘錄了圖算法最基本的BFS和DFS的實現以及A*、B*和Flood Fill的基本原理,由於原理不是十分難懂又有圖解過程,所以可以一次性掌握原理(雖然文字介紹相當簡要,不過好像也沒有什麼要說的),剩下的動手的問題。如果你有任何建議或者批評和補充,請留言指出,不勝感激,更多參考請移步互聯網。

 

 

 

參考:

MemoryGardenhttp://www.cppblog.com/MemoryGarden/articles/97979.html

阿凡盧http://www.cnblogs.com/luxiaoxun/archive/2012/08/05/2624115.html

 Create Chen http://www.cnblogs.com/technology/archive/2011/05/26/2058842.html

在迷茫中尋找四葉草 --MulinB的技術博客http://blog.csdn.net/mulinb/article/details/5939225

inysong:http://qinysong.iteye.com/blog/678941

 


發佈了26 篇原創文章 · 獲贊 79 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章