關鍵詞:尋路
** 1.深度優先搜索(Depth-First-Search):**
沿著樹的深度遍歷樹的節點,盡可能深的搜索樹的分支。當節點v的所有邊都己被探尋過,搜索將回溯到發現節點v的那條邊的起始節點。這一過程一直進行到已發現從源節點可達的所有節點為止。如果還存在未被發現的節點,則選擇其中一個作為源節點并重復以上過程,整個進程反復進行直到所有節點都被訪問為止。DFS屬于盲目搜索。
利用深度優先搜索算法可以產生目標圖的相應拓撲排序表,利用拓撲排序表可以方便的解決很多相關的圖論問題,如最大路徑問題等等。一般用堆數據結構來輔助實現DFS算法。
步驟
- 訪問頂點v;
- 依次從v的未被訪問的鄰接點出發,對圖進行深度優先遍歷;直至圖中和v有路徑相通的頂點都被訪問;
- 若此時圖中尚有頂點未被訪問,則從一個未被訪問的頂點出發,重新進行深度優先遍歷,直到圖中所有頂點均被訪問過為止。
對上面的圖G1進行深度優先遍歷,從頂點A開始。
第1步:訪問A。
第2步:訪問(A的鄰接點)C。
在第1步訪問A之后,接下來應該訪問的是A的鄰接點,即"C,D,F"中的一個。但在本文的實現中,頂點ABCDEFG是按照順序存儲,C在"D和F"的前面,因此,先訪問C。
第3步:訪問(C的鄰接點)B。
在第2步訪問C之后,接下來應該訪問C的鄰接點,即"B和D"中一個(A已經被訪問過,就不算在內)。而由于B在D之前,先訪問B。
第4步:訪問(C的鄰接點)D。
在第3步訪問了C的鄰接點B之后,B沒有未被訪問的鄰接點;因此,返回到訪問C的另一個鄰接點D。
第5步:訪問(A的鄰接點)F。
前面已經訪問了A,并且訪問完了"A的鄰接點B的所有鄰接點(包括遞歸的鄰接點在內)";因此,此時返回到訪問A的另一個鄰接點F。
第6步:訪問(F的鄰接點)G。
第7步:訪問(G的鄰接點)E。
因此訪問順序是:A -> C -> B -> D -> F -> G -> E
下面以"有向圖"為例,來對深度優先搜索進行演示。
對上面的圖G2進行深度優先遍歷,從頂點A開始。
第2步:訪問B。
在訪問了A之后,接下來應該訪問的是A的出邊的另一個頂點,即頂點B。
第3步:訪問C。
在訪問了B之后,接下來應該訪問的是B的出邊的另一個頂點,即頂點C,E,F。在本文實現的圖中,頂點ABCDEFG按照順序存儲,因此先訪問C。
第4步:訪問E。
接下來訪問C的出邊的另一個頂點,即頂點E。
第5步:訪問D。
接下來訪問E的出邊的另一個頂點,即頂點B,D。頂點B已經被訪問過,因此訪問頂點D。
第6步:訪問F。
接下應該回溯"訪問A的出邊的另一個頂點F"。
第7步:訪問G。
因此訪問順序是:A -> B -> C -> E -> D -> F -> G
廣度優先搜索(Breadth-First-Search):
一種圖形搜索算法。簡單的說,BFS是從根節點開始,沿著樹(圖)的寬度遍歷樹(圖)的節點。如果所有節點均被訪問,則算法中止。BFS同樣屬于盲目搜索。一般用隊列數據結構來輔助實現BFS算法。
步驟:
- 首先將根節點放入隊列中。
- 從隊列中取出第一個節點,并檢驗它是否為目標。
如果找到目標,則結束搜尋并回傳結果。
否則將它所有尚未檢驗過的直接子節點加入隊列中。 - 若隊列為空,表示整張圖都檢查過了——亦即圖中沒有欲搜尋的目標。結束搜尋并回傳“找不到目標”。
- 重復步驟2。
下面以"無向圖"為例,來對廣度優先搜索進行演示。還是以上面的圖G1為例進行說明。
第1步:訪問A。
第2步:依次訪問C,D,F。
在訪問了A之后,接下來訪問A的鄰接點。前面已經說過,在本文實現中,頂點ABCDEFG按照順序存儲的,C在"D和F"的前面,因此,先訪問C。再訪問完C之后,再依次訪問D,F。
第3步:依次訪問B,G。
在第2步訪問完C,D,F之后,再依次訪問它們的鄰接點。首先訪問C的鄰接點B,再訪問F的鄰接點G。
第4步:訪問E。
在第3步訪問完B,G之后,再依次訪問它們的鄰接點。只有G有鄰接點E,因此訪問G的鄰接點E。
因此訪問順序是:A -> C -> D -> F -> B -> G -> E
下面以"有向圖"為例,來對廣度優先搜索進行演示。還是以上面的圖G2為例進行說明。
第1步:訪問A。
第2步:訪問B。
第3步:依次訪問C,E,F。
在訪問了B之后,接下來訪問B的出邊的另一個頂點,即C,E,F。前面已經說過,在本文實現中,頂點ABCDEFG按照順序存儲的,因此會先訪問C,再依次訪問E,F。
第4步:依次訪問D,G。
在訪問完C,E,F之后,再依次訪問它們的出邊的另一個頂點。還是按照C,E,F的順序訪問,C的已經全部訪問過了,那么就只剩下E,F;先訪問E的鄰接點D,再訪問F的鄰接點G。
因此訪問順序是:A -> B -> C -> E -> F -> D -> G
啟發式搜索(Heuristically Search):
又稱為有信息搜索(Informed Search),它是利用問題擁有的啟發信息來引導搜索,達到減少搜索范圍、降低問題復雜度的目的,這種利用啟發信息的搜索過程稱為啟發式搜索。
就是在狀態空間中的搜索對每一個搜索的位置進行評估,得到最好的位置,再從這個位置進行搜索直到目標。
這樣可以省略大量無謂的搜索路徑,提高了效率。在啟發式搜索中,對位置的估價(估價函數)是十分重要的
利用啟發式搜索自動尋路實例
<style type="text/css">
*{ margin:0; padding:0;}
li{ list-style:none;}
#map{
height:auto;
overflow:hidden;
margin:20px auto;
border:1px #ccc solid;
border-bottom:none;
border-right:none;
}
#map li{
box-sizing: border-box;
border:1px #ccc solid;
border-top:none;
border-left:none;
float:left;
}
#map li.sty1{ background:#e22841;}
#map li.sty2{ background:orange;}
#map li.sty3{ background:#00bcda;}
#btn{
width:160px;
height: 40px;
color: #fff;
text-align: center;
line-height: 40px;
font-size: 16px;
background-color: #00bcda;
border: none;
border-radius: 5px;
position:absolute;
left:0;
right: 0;
margin: auto;
cursor: pointer;
}
</style>
<body>
<ul id="map"></ul>
<span id="btn">快去解救公主!</span>
</body>
<script type="text/javascript">
var map = [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,1,0,0,0,0,0,3,0,0,0,0,2,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,3,3,3,0,3,3,3,0,3,0,
0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,3,3,0,3,3,3,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
];
var sizeGird = 24; // 設定網格尺寸
var speed = 100; // 設定移動速度
(function road(){
var oUl = document.getElementById('map');
var aLi = oUl.getElementsByTagName('li');
var oBtn = document.getElementById('btn');
var beginLi = oUl.getElementsByClassName('sty1');
var endLi = oUl.getElementsByClassName('sty2');
var cols = Math.sqrt(map.length);
var openArr = []; // 存放可能要走的路線
var closeArr = []; // 存放不允許走的路線
// 點擊按鈕初始化
init();
function init(){
createMap();
oBtn.onclick = function(){
openFn();
};
}
// 創建網格
function createMap(){
oUl.style.width = cols * (sizeGird) + 'px';
for(var i=0;i<map.length;i++){
var oLi = document.createElement('li');
oLi.style.width = sizeGird + 'px';
oLi.style.height = sizeGird + 'px';
oUl.appendChild(oLi);
if( map[i] == 1 ){
oLi.className = 'sty1';
openArr.push(oLi);
}
else if(map[i] == 2){
oLi.className = 'sty2';
}
else if(map[i] == 3){
oLi.className = 'sty3';
closeArr.push(oLi);
}
}
}
function openFn(){
var nowLi = openArr.shift();
if( nowLi == endLi[0] ){
showLine();
return;
}
closeFn(nowLi);
findLi(nowLi);
//console.log( openArr );
openArr.sort(function(li1,li2){
return li1.num - li2.num;
});
//console.log( openArr );
openFn(); // 遞歸操作,重復執行函數
}
// 過濾走過的路線
function closeFn(nowLi){
closeArr.push( nowLi );
}
// 找尋所有可能走的網格
function findLi(nowLi){
var result = [];
for(var i=0;i<aLi.length;i++){
if( filter(aLi[i]) ){
result.push( aLi[i] );
}
}
function filter(li){
for(var i=0;i<closeArr.length;i++){
if( closeArr[i] == li ){
return false;
}
}
for(var i=0;i<openArr.length;i++){
if( openArr[i] == li ){
return false;
}
}
return true;
}
// 找到當前網格周圍的八個網格
for(var i=0;i<result.length;i++){
if( (Math.abs(nowLi.offsetLeft - result[i].offsetLeft)<= sizeGird) && (Math. abs(nowLi.offsetTop - result[i].offsetTop)<= sizeGird) ){
result[i].num = f(result[i]);
result[i].parent = nowLi;
openArr.push( result[i] );
}
}
}
// 顯示路線
function showLine(){
var result = [];
var lastLi = closeArr.pop();
var iNow = 0;
findParent(lastLi);
function findParent(li){
result.unshift(li);
if( li.parent == beginLi[0] ){
return;
}
findParent(li.parent);
}
var timer = setInterval(function(){
result[iNow].style.background = '#e22841';
iNow++;
if(iNow == result.length){
clearInterval(timer);
}
},speed);
}
// 估價函數
function f(nodeLi){
return g(nodeLi) + h(nodeLi);
}
function g(nodeLi){
var a = beginLi[0].offsetLeft - nodeLi.offsetLeft;
var b = beginLi[0].offsetTop - nodeLi.offsetTop;
return Math.sqrt(a*a + b*b); // 利用了勾股定理
}
function h(nodeLi){
var a = endLi[0].offsetLeft - nodeLi.offsetLeft;
var b = endLi[0].offsetTop - nodeLi.offsetTop;
return Math.sqrt(a*a + b*b);
}
})(map,sizeGird,speed)
在線預覽實例 ?[請點這里!!!][]
[請點這里!!!]:http://htmlpreview.github.io/?https://github.com/ferrinte/algorithm/blob/master/road.html