在這裡介紹兩種基本的搜尋演算法:深度優先搜尋和廣度優先搜尋法,以樹的搜尋為例,深度優先搜尋法是優先擴充套件尚未擴充套件的且具有最大深度的結點;廣度優先搜尋法是在擴充套件完第k層的結點以後才擴充套件k+1層的結點。
深度優先搜尋法與前面講的回溯法差不多,主要的區別是回溯法在求解過程中不保留完整的樹結構,而深度優先搜尋則記下完整的搜尋樹,搜尋樹起記錄解路徑和狀態判重的作用。為了減少儲存空間,在深度優先搜尋中,用標誌的方法記錄訪問過的狀態,這種處理方法使得深度優先搜尋法與回溯法沒什麼區別了。在回溯法中,我們己分析了非遞迴的實現過程,在這裡就只討論深度優先的遞迴實現方法。
深度優先搜尋的遞迴實現過程:
procedure dfs(i);
for i:=1 to r do
if 子結點mr符合條件then 產生的子結點mr入棧;
if 子結點 mr 是目標結點 then 輸出
else dfs(i+1);
棧頂元素出棧(即刪去mr);
endif;
endfor;
在講解遞推法時,我們討論了用遞推法解騎土遊歷問題,在這裡我們再看看如何用深度優先搜尋法求解此題。
例1騎士遊歷:設有乙個n*m的棋盤,在棋盤上任一點有乙個中國象棋馬,馬走的規則為:1.馬走日字 2.馬只能向右走。當n,m 輸入之後,找出一條從左下角到右上角的路徑。例如:輸入 n=4,m=4,輸出:路徑的格式:(1,1)->(2,3)->(4,4),若不存在路徑,則輸出"no"
演算法分析:我們以4×4的棋盤為例進行分析,用樹形結構表示馬走的所有過程(如下圖),求從起點到終點的路徑,實際上就是從根結點開始深度優先搜尋這棵樹。
馬從(1,1)開始,
程式如下:
const
dx:array[1..4]of integer=(2,2,1,1);
dy:array[1..4]of integer=(1,-1,2,-2);
type
map=record
x,y:integer;
end;
vari,n,m:integer;
a:array[0..50]of map;
procedure dfs(i:integer);
var j:integer;
begin
for j:=1 to 4 do
if (a[i-1].x+dx[j]>0)and(a[i-1].x+dx[j]<=n) and(a[i-1].y+dy[j]>0)and(a[i-1].y+dy[j]<=n) then
begin
a[i].x:=a[i-1].x+dx[j];
a[i].y:=a[i-1].y+dy[j];
if (a[i].x=n)and(a[i].y=m)then
begin
write('(',1,',',1,')');
for j:=2 to i do write('->(',a[j].x,',',a[j].y,')');
halt;
end;
dfs(i+1);
a[i].x:=0;a[i].y:=0;
end;
end;
begin
a[1].x:=1;a[1].y:=1;
readln(n,m);
dfs(2);
writeln('no');
end.
從上面的例子我們可以看出,深度優先搜尋演算法有兩個特點:
1、己產生的結點按深度排序,深度大的結點先得到擴充套件,即先產生它的子結點。
2、深度大的結點是後產生的,但先得到擴充套件,即「後產生先擴充套件」,與棧的工作原理相同,因此用堆疊作為該演算法的主要資料結構,儲存產生的結點。
對於不同的問題,深度優先搜尋演算法基本上是一樣的,但在具體處理方法和程式設計技巧上又都不相同,甚至會有很大的差別。我們再看看另乙個例子。
題二 選數(存檔名:noip2002pj)
[問題描述]:已知 n 個整數 x1,x2,…,xn,以及乙個整數 k(k<n)。從n 個整數中任選 k 個整數相加,可分別得到一系列的和。例如當 n=4,k=3,4 個整數分別為 3,7,12,19 時,可得全部的組合與它們的和為:3+7+12=22 3+7+19=29 7+12+19=38 3+12+19=34。現在,要求你計算出和為素數共有多少種。例如上例,只有一種的和為素數:3+7+19=29。
[輸入]:鍵盤輸入,格式為:
n , k (1<=n<=20,k<n)
x1,x2,…,xn (1<=xi<=5000000)
[輸出]:螢幕輸出,格式為:
乙個整數(滿足條件的種數)。
[輸入輸出樣例]:
輸入:4 3
3 7 12 19
輸出:1
演算法分析:本題是求從n個數中選k個數的組合,並使其和為素數。求解此題時,先用深度優先搜尋法生成k個數的組合,再判斷k個數的和是否為素數,若為素數則總數加1。
在程式實現過程中,用陣列a存放輸入的n個數,用s表示k個數的和,ans表示和為素數的個數。為了避免不必要的搜尋,程式對搜尋過程進行了優化,限制搜尋範圍,在搜尋過程dfs(i,m)中,引數m為第i個數的上限,下限為n-k+i。
源程式:
varn,k,i: byte;
ans,s:longint;
a: array[1 .. 20] of longint;
procedure prime(s:longint);
vari:integer;
begin
i:=2;
while (sqr(i)<=s)and(s mod i<>0) do inc(i);
if sqr(i)>s then inc(ans)
end;
procedure dfs(i,m:byte);
varj:byte;
begin
inc(s,a[j]);
if i=k then prime(s)
else dfs(i+1,j+1);
dec(s,a[j])
endend;
begin
readln(n,k);
for i:=1 to n do read(a[i]);
ans:=0; s:=0;
dfs(1,1);
writeln(ans);
end.
從上面的兩個例子我們可以看出,用遞迴實現深度優先搜尋比非遞迴更加方便。
在使用深度搜尋法解題時,搜尋的效率並不高,所以要重視對演算法的優化,盡可能的減少搜尋範圍,提高程式的速度。
深度優先搜尋和廣度優先搜尋
深度優先的思想是先記住當前的起點,然後選定乙個方向一條道走到黑,若失敗則回到起點再選定另外乙個方向走到黑。廣度優先的思想是記住當前的起點,然後選定各個方向的相鄰點作為新的起點,再繼續。可以看出,深度優先和廣度優先都需要記住當前的起點,不同的是深度優先每次只需要記住乙個方向的相鄰點,廣度優先則要記住所...
廣度優先搜尋和深度優先搜尋
dbf深度優先搜尋,最經典的方法,可以使用遞迴來實現。結構體定義 typedef char vertextype typedef int edgetype define maxvex 100 define infinite 65535 typedef struct mgraph 測試函式如下 mgr...
深度優先搜尋和廣度優先搜尋
定義 圖 graph 是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為 g v,e 其中,g表示乙個圖,v是圖g中頂點的集合,e是圖g中邊的集合.簡單點的說 圖由節點和邊組成。乙個節點可能與眾多節點直接相連,這些節點被稱為鄰居。如二叉樹就為乙個簡單的圖 廣度優先搜尋演算法 breadth f...