BFS(一):廣度優先搜索的基本思想


      廣度優先搜索BFS(Breadth First Search)也稱為寬度優先搜索,它是一種先生成的結點先擴展的策略。

      在廣度優先搜索算法中,解答樹上結點的擴展是按它們在樹中的層次進行的。首先生成第一層結點,同時檢查目標結點是否在所生成的結點中,如果不在,則將所有的第一層結點逐一擴展,得到第二層結點,並檢查第二層結點是否包含目標結點,……,對層次為n+1的任一結點進行擴展之前,必須先考慮層次完層次為n的結點的每種可能的狀態。因此,對於同一層結點來說,求解問題的價值是相同的,可以按任意順序來擴展它們。通常采用的原則是先生成的結點先擴展。

      為了便於進行搜索,要設置一個表存儲所有的結點。由於在廣度優先搜索算法中,要滿足先生成的結點先擴展的原則,所以存儲結點的表一般采用隊列這種數據結構。

      在編寫程序時,可用數組q模擬隊列。front和rear分別表示隊頭指針和隊尾指針,初始時front=rear=0。

      元素x入隊操作為  q[rear++]=x;

      元素x出隊操作為  x =q[front++];

      廣度優先搜索算法的搜索步驟一般是:

      (1)從隊列頭取出一個結點,檢查它按照擴展規則是否能夠擴展,如果能則產生一個新結點。

       (2)檢查新生成的結點,看它是否已在隊列中存在,如果新結點已經在隊列中出現過,就放棄這個結點,然后回到第(1)步。否則,如果新結點未曾在隊列中出現過,則將它加入到隊列尾。

      (3)檢查新結點是否目標結點。如果新結點是目標結點,則搜索成功,程序結束;若新結點不是目標結點,則回到第(1)步,再從隊列頭取出結點進行擴展。

      最終可能產生兩種結果:找到目標結點,或擴展完所有結點而沒有找到目標結點。

      如果目標結點存在於解答樹的有限層上,廣度優先搜索算法一定能保證找到一條通向它的最佳路徑,因此廣度優先搜索算法特別適用於只需求出最優解的問題。當問題需要給出解的路徑,則要保存每個結點的來源,也就是它是從哪一個節點擴展來的。

      對於廣度優先搜索算法來說,問題不同則狀態結點的結構和結點擴展規則是不同的,但搜索的策略是相同的。廣度優先搜索算法的框架一般如下:

void  BFS()

{

    隊列初始化;

    初始結點入隊;

    while (隊列非空)

    {  

          隊頭元素出隊,賦給current;

          while  (current 還可以擴展)

          {

              由結點current擴展出新結點new;

              if  (new 重復於已有的結點狀態) continue;

              new結點入隊;

              if  (new結點是目標狀態)

              {

                    置flag= true;    break; 

               }

          }

      }

}

       對於不同的問題,用廣度優先搜索法的算法基本上都是一樣的。但表示問題狀態的結點數據結構、新結點是否為目標結點和是否為重復結點的判斷等方面則有所不同。對具體的問題需要進行具體分析,這些函數要根據具體問題進行編寫。

【例1】黑色方塊

      有一個寬為W、高為H的矩形平面,用黑色和紅色兩種顏色的方磚鋪滿。一個小朋友站在一塊黑色方塊上開始移動,規定移動方向有上、下、左、右四種,且只能在黑色方塊上移動(即不能移到紅色方塊上)。編寫一個程序,計算小朋友從起點出發可到達的所有黑色方磚的塊數(包括起點)。

      例如,如圖1所示的矩形平面中,“#”表示紅色磚塊,“.”表示黑色磚塊,“@”表示小朋友的起點,則小朋友能走到的黑色方磚有28塊。

      (1)編程思路。

      采用廣度優先搜索法解決這個問題。

      用數組q模擬隊列操作,front為隊頭指針,rear為隊尾指針,初始時front=rear=0。

      入隊操作為 q[rear++]=cur;

      出隊操作為 cur=q[front++]。

      程序中定義方磚的位置坐標(x,y)為Node類型,定義數組int visit[N][N]標記某方磚是否已走過,visit[i][j]=0表示坐標(i,j)處的方磚未走過,visit[i][j]=1表示坐標(i,j)處的方磚已走過。初始時visit數組的所有元素值均為0。

      具體算法步驟為:

      ① 將出發點(startx,starty)入隊列q,且置visit[startx][starty]=1,表示該處的方磚已被處理,以后不再重復搜索。

      ② 將隊列q的隊頭元素出棧,得到一個當前方磚cur,黑色方磚計數(sum++),沿其上、下、左、右四個方向上搜索未走過的黑色方磚,將找到的黑色方磚的坐標入隊列q。

      ③ 重復執行②,直至隊列q為空,則求出了所有能走過的黑色方磚數。

      (2)源程序。

#include <iostream>

using namespace std;

#define N 21

struct Node

{

       int x;

       int y;

};

int dx[4]={-1,1,0,0};

int dy[4]={0,0,-1,1};

char map[N][N];

int visit[N][N];

int bfs(int startx, int starty,int w,int h)

{

       Node q[N*N],cur,next;   // q為隊列

       int  front,rear;        // front為隊頭指針,rear為隊尾指針

       int i,x,y,sum;    

       front=rear=0;           // 隊列q初始化

       sum=0;

       cur.x=startx;   cur.y=starty; 

       visit[startx][starty]=1;

       q[rear++]=cur;          // 初始結點入隊

       while(rear!=front)      // 隊列不為空

       {

              cur=q[front++];     // 隊頭元素出隊

              sum++;              // 方磚計數

              for (i=0;i<4;i++)

              {

                  x=cur.x+dx[i];  y=cur.y+dy[i];

                 if(x >=0 && x<h && y>=0 && y<w && map[x][y]!='#' && visit[x][y]==0)

                 {

                        visit[x][y] = 1;

                        next.x=x;  next.y=y;  // 由cur擴展出新結點next

                        q[rear++]=next;       // next結點入隊

                  } 

              }

       }

       return sum;

}

int main()

{

   int i,j,pos_x,pos_y,w,h,sum;

   while(1)

   {

       cin>>w>>h;

        if (w==0 && h==0) break;

       for(i=0;i<h;i++)

       {

          for(j=0;j<w;j++)

          {

              cin>>map[i][j];

              if (map[i][j]=='@')

             {

                pos_x = i;

                pos_y = j;

             }

              visit[i][j] = 0;

         }

      }

      sum=bfs(pos_x, pos_y,w,h);

      cout<<sum<<endl;

   }

   return 0;

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM