Dijkstra算法暢遊星際
来源:互联网 发布:cea热力学软件 编辑:程序博客网 时间:2024/05/16 11:33
今天淩晨,不知大家有沒有去看雙子座流星雨。我和我宿舍的哥們兒可是頂著嚴寒都去了的。報紙上說高峰時會達到每分鐘200顆,呵呵,好像沒有這麽誇張。流星不那麽容易被看到,於是我們便只得一直仰著脖子;仰酸了,就只好忍著刺骨的樹膠跑道躺在地上。不過這麽苦還是挺值得的,因爲流星真的好漂亮啊!特別是那種特別大的,有的火紅、有的白紫,隕落的時候照亮了穹隆的西北角;但更多的是那些小的,它們偷偷地、迅速地滑過天際,你反應都還來不及,就別提許願了呵呵!
看得累了,眼睛花了,我就幻想著自己開著星際飛船在每顆星星閒來回地馳騁。嘿,我突然想問你一個很古怪的問題,如果這些星星之間已經用星際隧道連接成了一個網絡,每條隧道的距離以及耗油量都不同,而你只能從星級隧道中飛行,那你應該怎麽尋找從A星到B星的最便捷的路徑呢?嘿嘿,讓我來告訴你思路吧。
實際上,我剛才提出的那個星際模型是一個典型的圖(graph)。什麽是圖呢?圖就是一個有限的集合,包括了一群節點(vertex)和把節點連成一個網絡的帶了權值的邊(edge),數學圖論裏把它記作G=(V,E)。現在來抽象我們的問題吧:我們就是要在一個圖内尋找某一個到另一個節點的路徑,使得沿著這條路徑走經過的邊的權值之和最小。在尋找最短路徑的諸多算法中,最有名的應該是Dijkstra算法了。在我學圖的那堂課上,這個算法讓我細細品味了很久。
下面的代碼摘自我的一份C++作業,其中的圖是用一個二維等長寬數組表示的。我故意刪掉了註釋,看看你能不能自己看懂它。
#include "iostream"
#define VEXNUM 6
#define INFIN 32767
void Dijkstra(int (*ThisGraph)[VEXNUM], int from, int to) {
int Dist[VEXNUM], Path[VEXNUM], IsVisited[VEXNUM];
for(int i = 0; i < VEXNUM; i++) {
Dist[i] = ThisGraph[from][i];
IsVisited[i] = 0;
if(i != from && Dist[i] <INFIN)
Path[i] = from;
else
Path[i] = -1;
}
IsVisited[from] = 1;
Dist[from] = 0;
for(i = 0; i < VEXNUM - 1; i++) {
int min = INFIN;
int u = from;
for(int j = 0; j < VEXNUM; j++)
if(!IsVisited[j] && Dist[j] < min) {
u = j;
min = Dist[j];
}
IsVisited[u] = 1;
for(int w = 0; w < VEXNUM; w++) {
if(!IsVisited[w] && ThisGraph[u][w] < INFIN && Dist[u] + ThisGraph[u][w] < Dist[w]) {
Dist[w] = Dist[u] + ThisGraph[u][w];
Path[w] = u;
}
}
}
std::cout<<"從"<<from<<"點到"<<to<<"點的最短路徑為(從右到左輸出):"<<std::endl;
i = to;
while(i != from) {
std::cout<<i<<"<-";
i = Path[i];
}
std::cout<<i;
std::cout<<std::endl<<"長度為:"<<Dist[to];
}
你結合著看一個例子,這是一個圖的典型的鄰接矩陣:
INFIN | INFIN | 10 | INFIN | 30 | 100 |
INFIN | INFIN | 5 | INFIN | INFIN | INFIN |
INFIN | INFIN | INFIN | 50 | INFIN | INFIN |
INFIN | INFIN | INFIN | INFIN | INFIN | 10 |
INFIN | INFIN | INFIN | 20 | INFIN | 60 |
INFIN | INFIN | INFIN | INFIN | INFIN | INFIN |
點 | i=1 | i=2 | i=3 | i=4 | i=5 |
1 | INFIN | INFIN | INFIN | INFIN | INFIN |
2 | 10 0->2 | ||||
3 | INFIN | 60 0->2->3 | 50 0->4->3 | ||
4 | 30 0->4 | 30 0->4 | |||
5 | 100 0->5 | 100 0->5 | 90 0->4->5 | 60 0->4->3->5 | |
最短 | 2 | 4 | 3 | 5 | |
IsVisited | 0、2 | 0、2、4 | 0、2、3、4 | 0、2、3、4、5 |
上面這個例子是查找從0點出發到達所有其他頂點的最短路徑。看懂了嗎?實際上,要求從某個源點到某個頂點的最短距離,必須遍歷圖的所有頂點,把源點到所有頂點的最短距離都求出來才行。上面的Dist數組就是用來存放這所有的最短距離的。Dist初態就是從源點到其它點的直接路徑的權值。但是你也看出來了,大多數情況下,從一個點到另一個點的最短路徑並不是直接路徑,怎麽辦?
因此這裡另外有一個取巧的地方,就是引進了IsVisited這個數組。它實際上是一個集合,如果它包含了i點,那麽IsVisited[i]就標記為1,否則為0。在初態,IsVisited裏標記為1的只有源點,以後每求得一個最短路徑,就往IsVisited集合中增加一個點。當所有頂點都被加到IsVisited集合中,就意味著遍歷完畢,算法結束了。
看懂了IsVisited的作用,你就理解了Dijkstra。我們先看最簡單的情況:和0點直接相鄰的點有2、4、5,並且到2的權值最小,那麽,我們可以打包票說,從0到2之間最短路徑就是這條直接相連的路徑了。那麽,我們可以把2標記為IsVisited了。但是,我們在這一步卻只能斷定這一點,因爲你無法保證從0到4或者5的直接權值一定最小。請你好好回味一下,別急著往下看。我覺得,所有複雜的東西,把它最簡單的情況搞透徹,就弄懂了一大半!
以下的每一步實際上都是第一步最簡單情況的擴展,只是另外多考慮到了之前已放入IsVisited集合裏的頂點。因爲:下一次查找到的最短路徑或者是直接路徑0->2,或者是中間經轉了IsVisited中頂點而到達2點的路徑,反正短的那條就一定是了!你懷疑這句話嗎?如果不是這樣的話,那麽就意味著最短路徑上存在一點不在IsVisited中了。但是別忘了,我們是按照路徑長度遞增順序來產生每個最短路徑的,所以長度比這條路徑短的所有路徑都已經產生了,它們的終點一定在IsVisited中!假設推翻了……你可以大膽地挑出那條最短的路徑,把它的終點放進IsVisited集合裏,繼續下一輪查找。只要圖是連通的,每一次都能找到到達某一個點的最短路徑,那麽查找VEXNUM-1次就OK啦!
説道流星,你可千萬別以爲查找最短路徑只是存在于太空科幻小説中,其實就在我們的地球上,它就有著極其廣闊的運用背景。Dijkstra已經廣泛地應用于AI中——有圖的地方,就有Dijkstra。把Dijkstra算法稍加修改,還能得到另一個很有名的Prim算法,用於建立最小支撐樹。
哎呀差點忘了,我們剛才只是說找到了最短路徑的大小,但是最短路徑經過了哪些頂點還沒求呢!你可能早看出來了,這個Path數組就是專門來干這個的嘿嘿!它保存的是到達每條最短路徑終點的前一個頂點。呵呵,很巧是吧。不過我上面的代碼可以按照從終點到起點的順序把頂點打印出來,你恐怕看了相當不爽——“涕淌君怎麽年紀輕輕就這麽懶!”好吧,就留給你完成吧!
- Dijkstra算法暢遊星際
- Dijkstra算法
- dijkstra算法
- Dijkstra算法
- Dijkstra算法
- Dijkstra算法
- Dijkstra算法
- Dijkstra 算法
- Dijkstra算法
- Dijkstra算法
- Dijkstra算法
- Dijkstra 算法
- Dijkstra 算法
- dijkstra算法
- Dijkstra 算法
- Dijkstra算法
- Dijkstra算法
- Dijkstra算法
- 文档分析
- Use P/Invoke to Develop a .NET Base Class Library for Serial Device Communications
- left join 分析
- VS.NET 2003 控件命名规范
- Java开发者必去的20个英文技术网站
- Dijkstra算法暢遊星際
- #define 的另类用法
- blog 启用
- 年底到了,工作换了
- 级联删除的触发器代码解释
- 在控制台程序中隐藏控制台窗口!
- 老友归来--delphi2005试用手记1
- WinCE系统下BootLoader的开发【转载】
- 消灭隐藏在IE中的隐患!