深度优先搜索和广度优先搜索的比较与分析
一)深度優(yōu)先搜索的特點是:
(1)無論問題的內(nèi)容和性質(zhì)以及求解要求如何不同,它們的程序結(jié)構(gòu)都是相同的,即都是深度優(yōu)先算法(一)和深度優(yōu)先算法(二)中描述的算法結(jié)構(gòu),不相同的僅僅是存儲結(jié)點數(shù)據(jù)結(jié)構(gòu)和產(chǎn)生規(guī)則以及輸出要求。
(2)深度優(yōu)先搜索法有遞歸以及非遞歸兩種設(shè)計方法。一般的,當(dāng)搜索深度較小、問題遞歸方式比較明顯時,用遞歸方法設(shè)計好,它可以使得程序結(jié)構(gòu)更簡捷易懂。當(dāng)搜索深度較大時,當(dāng)數(shù)據(jù)量較大時,由于系統(tǒng)堆棧容量的限制,遞歸容易產(chǎn)生溢出,用非遞歸方法設(shè)計比較好。
(3)深度優(yōu)先搜索方法有廣義和狹義兩種理解。廣義的理解是,只要最新產(chǎn)生的結(jié)點(即深度最大的結(jié)點)先進(jìn)行擴(kuò)展的方法,就稱為深度優(yōu)先搜索方法。在這種理解情況下,深度優(yōu)先搜索算法有全部保留和不全部保留產(chǎn)生的結(jié)點的兩種情況。而狹義的理解是,僅僅只保留全部產(chǎn)生結(jié)點的算法。本書取前一種廣義的理解。不保留全部結(jié)點的算法屬于一般的回溯算法范疇。保留全部結(jié)點的算法,實際上是在數(shù)據(jù)庫中產(chǎn)生一個結(jié)點之間的搜索樹,因此也屬于圖搜索算法的范疇。
(4)不保留全部結(jié)點的深度優(yōu)先搜索法,由于把擴(kuò)展望的結(jié)點從數(shù)據(jù)庫中彈出刪除,這樣,一般在數(shù)據(jù)庫中存儲的結(jié)點數(shù)就是深度值,因此它占用的空間較少,所以,當(dāng)搜索樹的結(jié)點較多,用其他方法易產(chǎn)生內(nèi)存溢出時,深度優(yōu)先搜索不失為一種有效的算法。
(5)從輸出結(jié)果可看出,深度優(yōu)先搜索找到的第一個解并不一定是最優(yōu)解。
如果要求出最優(yōu)解的話,一種方法將是后面要介紹的動態(tài)規(guī)劃法,另一種方法是修改原算法:把原輸出過程的地方改為記錄過程,即記錄達(dá)到當(dāng)前目標(biāo)的路徑和相應(yīng)的路程值,并與前面已記錄的值進(jìn)行比較,保留其中最優(yōu)的,等全部搜索完成后,才把保留的最優(yōu)解輸出。
?
二、廣度優(yōu)先搜索法的顯著特點是:
(1)在產(chǎn)生新的子結(jié)點時,深度越小的結(jié)點越先得到擴(kuò)展,即先產(chǎn)生它的子結(jié)點。為使算法便于實現(xiàn),存放結(jié)點的數(shù)據(jù)庫一般用隊列的結(jié)構(gòu)。
(2)無論問題性質(zhì)如何不同,利用廣度優(yōu)先搜索法解題的基本算法是相同的,但數(shù)據(jù)庫中每一結(jié)點內(nèi)容,產(chǎn)生式規(guī)則,根據(jù)不同的問題,有不同的內(nèi)容和結(jié)構(gòu),就是同一問題也可以有不同的表示方法。
(3)當(dāng)結(jié)點到跟結(jié)點的費(fèi)用(有的書稱為耗散值)和結(jié)點的深度成正比時,特別是當(dāng)每一結(jié)點到根結(jié)點的費(fèi)用等于深度時,用廣度優(yōu)先法得到的解是最優(yōu)解,但如果不成正比,則得到的解不一定是最優(yōu)解。這一類問題要求出最優(yōu)解,一種方法是使用后面要介紹的其他方法求解,另外一種方法是改進(jìn)前面深度(或廣度)優(yōu)先搜索算法:找到一個目標(biāo)后,不是立即退出,而是記錄下目標(biāo)結(jié)點的路徑和費(fèi)用,如果有多個目標(biāo)結(jié)點,就加以比較,留下較優(yōu)的結(jié)點。把所有可能的路徑都搜索完后,才輸出記錄的最優(yōu)路徑。
(4)廣度優(yōu)先搜索算法,一般需要存儲產(chǎn)生的所有結(jié)點,占的存儲空間要比深度優(yōu)先大得多,因此程序設(shè)計中,必須考慮溢出和節(jié)省內(nèi)存空間得問題。
(5)比較深度優(yōu)先和廣度優(yōu)先兩種搜索法,廣度優(yōu)先搜索法一般無回溯操作,即入棧和出棧的操作,所以運(yùn)行速度比深度優(yōu)先搜索算法法要快些。
?總之,一般情況下,深度優(yōu)先搜索法占內(nèi)存少但速度較慢,廣度優(yōu)先搜索算法占內(nèi)存多但速度較快,在距離和深度成正比的情況下能較快地求出最優(yōu)解。因此在選擇用哪種算法時,要綜合考慮。決定取舍。
?
?
二、基本搜索算法比較和搜索算法的優(yōu)化
?
搜索算法是利用計算機(jī)的高性能來有目的的窮舉一個問題的部分或所有的可能情況,從而求出問題的解的一種方法。搜索過程實際上是根據(jù)初始條件和擴(kuò)展規(guī)則構(gòu)造一棵解答樹并尋找符合目標(biāo)狀態(tài)的節(jié)點的過程。所有的搜索算法從其最終的算法實現(xiàn)上來看,都可以劃分成兩個部分──控制結(jié)構(gòu)和產(chǎn)生系統(tǒng),而所有的算法的優(yōu)化和改進(jìn)主要都是通過修改其控制結(jié)構(gòu)來完成的。現(xiàn)在主要對其控制結(jié)構(gòu)進(jìn)行討論,因此對其產(chǎn)生系統(tǒng)作如下約定:
Function?ExpendNode(Situation:Tsituation;ExpendWayNo:Integer):TSituation;
表示對給出的節(jié)點狀態(tài)Sitution采用第ExpendWayNo種擴(kuò)展規(guī)則進(jìn)行擴(kuò)展,并且返回擴(kuò)展后的狀態(tài)。
(本文所采用的算法描述語言為類Pascal。)
第一部分 基本搜索算法
一、回溯算法
回溯算法是所有搜索算法中最為基本的一種算法,其采用了一種“走不通就掉頭”思想作為其控制結(jié)構(gòu),其相當(dāng)于采用了先根遍歷的方法來構(gòu)造解答樹,可用于找解或所有解以及最優(yōu)解。具體的算法描述如下:(略)
范例:一個M*M的棋盤上某一點上有一個馬,要求尋找一條從這一點出發(fā)不重復(fù)的跳完棋盤上所有的點的路線。
評價:回溯算法對空間的消耗較少,當(dāng)其與分枝定界法一起使用時,對于所求解在解答樹中層次較深的問題有較好的效果。但應(yīng)避免在后繼節(jié)點可能與前繼節(jié)點相同的問題中使用,以免產(chǎn)生循環(huán)。
二、深度搜索與廣度搜索
深度搜索與廣度搜索的控制結(jié)構(gòu)和產(chǎn)生系統(tǒng)很相似,唯一的區(qū)別在于對擴(kuò)展節(jié)點選取上。由于其保留了所有的前繼節(jié)點,所以在產(chǎn)生后繼節(jié)點時可以去掉一部分重復(fù)的節(jié)點,從而提高了搜索效率。這兩種算法每次都擴(kuò)展一個節(jié)點的所有子節(jié)點,而不同的是,深度搜索下一次擴(kuò)展的是本次擴(kuò)展出來的子節(jié)點中的一個,而廣度搜索擴(kuò)展的則是本次擴(kuò)展的節(jié)點的兄弟節(jié)點。在具體實現(xiàn)上為了提高效率,所以采用了不同的數(shù)據(jù)結(jié)構(gòu).
三、初探隊與廣度優(yōu)先搜索:
1、隊的定義:
隊是特殊的線性表之一,它只允許在隊的一端插入,在隊的另一端刪除。插入一端叫隊尾(T),刪除一端叫隊首(H),沒有任何元素的隊叫做空隊。隊列遵循"先進(jìn)先出"原則,排隊購物、買票等,就是最常見的隊。
2、隊的基本操作:
(1)隊的描述:
? type queue=array[1..100] of integer;
var a:queue; {定義數(shù)組}
h,d:integer; {隊首、隊尾指針}
(2) 初始化(圖1):
procedure start;
begin
h:=1; d:=1;
end;
(3) 入隊操作(圖2):
procedure enter;
begin
read(s); {讀入數(shù)據(jù)}
inc(d); {隊尾加一}
a[d]:=s;
end;
(4) 出隊操作(圖3):
procedure out;
begin
inc(h); {隊首加一}
a[h]:=0;
end;
廣度優(yōu)先搜索類似于樹的按層次遍歷的過程。它和隊有很多相似之處,運(yùn)用了隊的許多思想,其實就是對隊的深入一步研究,它的基本操作和隊列幾乎一樣。
第二部分 搜索算法的優(yōu)化
一、雙向廣度搜索
廣度搜索雖然可以得到最優(yōu)解,但是其空間消耗增長太快。但如果從正反兩個方向進(jìn)行廣度搜索,理想情況下可以減少二分之一的搜索量,從而提高搜索速度。
范例:移動一個只含字母A和B的字符串中的字母,給定初始狀態(tài)為(a)表,目標(biāo)狀態(tài)為(b)表,給定移動規(guī)則為:只能互相對換相鄰字母。請找出一條移動最少步數(shù)的辦法。
AABBAA????????? BAAAAB
?? (a)?????????? (b)
問題分析:從初始狀態(tài)和目標(biāo)狀態(tài)均按照廣度優(yōu)先搜索擴(kuò)展接點,當(dāng)達(dá)到以下狀態(tài)時,出現(xiàn)相交點,如圖(c),接點序號表示生成順序。
雙向擴(kuò)展結(jié)點:
利用雙向搜索對廣度搜索算法的改進(jìn):
1、添加一張節(jié)點表,作為反向擴(kuò)展表。
2、在while循環(huán)體中在正向擴(kuò)展代碼后加入反向擴(kuò)展代碼,其擴(kuò)展過程不能與正向過程共享一個for循環(huán)。
3、在正向擴(kuò)展出一個節(jié)點后,需在反向表中查找是否有重合節(jié)點。反向擴(kuò)展時與之相同。
對雙向廣度搜索算法的改進(jìn): 略微修改一下控制結(jié)構(gòu),每次while循環(huán)時只擴(kuò)展正反兩個方向中節(jié)點數(shù)目較少的一個,可以使兩邊的發(fā)展速度保持一定的平衡,從而減少總擴(kuò)展節(jié)點的個數(shù),加快搜索速度。
二、分支定界
分支定界實際上是A*算法的一種雛形,其對于每個擴(kuò)展出來的節(jié)點給出一個預(yù)期值,如果這個預(yù)期值不如當(dāng)前已經(jīng)搜索出來的結(jié)果好的話,則將這個節(jié)點(包括其子節(jié)點)從解答樹中刪去,從而達(dá)到加快搜索速度的目的。
范例:在一個商店中購物,設(shè)第I種商品的價格為Ci。但商店提供一種折扣,即給出一組商品的組合,如果一次性購買了這一組商品,則可以享受較優(yōu)惠的價格。現(xiàn)在給出一張購買清單和商店所提供的折扣清單,要求利用這些折扣,使所付款最少。
問題分析:顯然,折扣使用的順序與最終結(jié)果無關(guān),所以可以先將所有的折扣按折扣率從大到小排序,然后采用回溯法的控制結(jié)構(gòu),對每個折扣從其最大可能使用次數(shù)向零遞減搜索,設(shè)A為已打完折扣后優(yōu)惠的價格,C為當(dāng)前未打折扣的商品零售價之和,則其預(yù)期值為A+a*C,其中a為下一個折扣的折扣率。如當(dāng)前已是最后一個折扣,則a=1。
對回溯算法的改進(jìn):
1、添加一個全局變量BestAnswer,記錄當(dāng)前最優(yōu)解。
2、在每次生成一個節(jié)點時,計算其預(yù)期值,并與BestAnswer比較。如果不好,則調(diào)用回溯過程。
三、A*算法
A*算法中更一般的引入了一個估價函數(shù)f,其定義為f=g+h。其中g(shù)為到達(dá)當(dāng)前節(jié)點的耗費(fèi),而h表示對從當(dāng)前節(jié)點到達(dá)目標(biāo)節(jié)點的耗費(fèi)的估計。其必須滿足兩個條件:
1、h必須小于等于實際的從當(dāng)前節(jié)點到達(dá)目標(biāo)節(jié)點的最小耗費(fèi)h*。
2、f必須保持單調(diào)遞增。
A*算法的控制結(jié)構(gòu)與廣度搜索的十分類似,只是每次擴(kuò)展的都是當(dāng)前待擴(kuò)展節(jié)點中f值最小的一個,如果擴(kuò)展出來的節(jié)點與已擴(kuò)展的節(jié)點重復(fù),則刪去這個節(jié)點。如果與待擴(kuò)展節(jié)點重復(fù),如果這個節(jié)點的估價函數(shù)值較小,則用其代替原待擴(kuò)展節(jié)點,具體算法描述如下:
范例:一個3*3的棋盤中有1-8八個數(shù)字和一個空格,現(xiàn)給出一個初始態(tài)和一個目標(biāo)態(tài),要求利用這個空格,用最少的步數(shù),使其到達(dá)目標(biāo)態(tài)。
問題分析:預(yù)期值定義為h=|x-dx|+|y-dy|。
估價函數(shù)定義為f=g+h。
<Type>
Node(節(jié)點類型)=Record
Situtation:TSituation(當(dāng)前節(jié)點狀態(tài));
g:Integer;(到達(dá)當(dāng)前狀態(tài)的耗費(fèi))
h:Integer;(預(yù)計的耗費(fèi))
f:Real;(估價函數(shù)值)
Last:Integer;(父節(jié)點)
End
<Var>
List(節(jié)點表):Array[1..Max(最多節(jié)點數(shù))] of Node(節(jié)點類型);
open(總節(jié)點數(shù)):Integer;
close(待擴(kuò)展節(jié)點編號):Integer;
New-S:Tsituation;(新節(jié)點)
<Init>
List<-0;
open<-1;
close<-0;
List[1].Situation<- 初始狀態(tài);
<Main Program>
While (close<open(還有未擴(kuò)展節(jié)點)) and
(open<Max(空間未用完)) and
(未找到目標(biāo)節(jié)點) do
Begin
Begin
close:=close+1;
For I:=close+1 to open do (尋找估價函數(shù)值最小的節(jié)點)
Begin
if List[i].f<List[close].f then
Begin
交換List[i]和List[close];
End-If;
End-For;
End;
For I:=1 to TotalExpendMethod do(擴(kuò)展一層子節(jié)點)
Begin
New-S:=ExpendNode(List[close].Situation,I)
If Not (New-S in List[1..close]) then
(擴(kuò)展出的節(jié)點未與已擴(kuò)展的節(jié)點重復(fù))
Begin
If Not (New-S in List[close+1..open]) then
(擴(kuò)展出的節(jié)點未與待擴(kuò)展的節(jié)點重復(fù))
Begin
open:=open+1;
List[open].Situation:=New-S;
List[open].Last:=close;
List[open].g:=List[close].g+cost;
List[open].h:=GetH(List[open].Situation);
List[open].f:=List[open].h+List[open].g;
End-If
Else Begin
If List[close].g+cost+GetH(New-S)<List[same].f then
(擴(kuò)展出來的節(jié)點的估價函數(shù)值小于與其相同的節(jié)點)
Begin
List[same].Situation:= New-S;
List[same].Last:=close;
List[same].g:=List[close].g+cost;
List[same].h:=GetH(List[open].Situation);
List[same].f:=List[open].h+List[open].g;
End-If;
End-Else;
End-If
End-For;
End-While;
對A*算法的改進(jìn)--分階段A*:
當(dāng)A*算法出現(xiàn)數(shù)據(jù)溢出時,從待擴(kuò)展節(jié)點中取出若干個估價函數(shù)值較小的節(jié)點,然后放棄其余的待擴(kuò)展節(jié)點,從而可以使搜索進(jìn)一步的進(jìn)行下去。
總結(jié)
以上是生活随笔為你收集整理的深度优先搜索和广度优先搜索的比较与分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 字符串去特定字符
- 下一篇: DFS与BFS的总结