-DFS(Depth First Search):深度優(yōu)先搜索
訪問(wèn)完一個(gè)頂點(diǎn)的所有鄰接點(diǎn)之后,會(huì)按原路返回,對(duì)應(yīng)著堆棧、出棧
void DFS ( Vertex V )
{ visited [ V ] = true ;
for ( V的每個(gè)鄰接點(diǎn)W )
if ( !visited [ W ] )
DFS ( W ) ;
}
深度優(yōu)先搜索相當(dāng)于樹(shù)的先序遍歷
若有N個(gè)頂點(diǎn)、E條邊,時(shí)間復(fù)雜度為:
用鄰接表存儲(chǔ)圖,有 O ( N + E )
用鄰接矩陣存儲(chǔ)圖,有 O ( N的平方 )
-BFS(Breadth First Search):廣度優(yōu)先搜索(相當(dāng)于樹(shù)的層序遍歷)
樹(shù)是一種特殊的圖,類(lèi)似于樹(shù)的層序遍歷(隊(duì)列),圖的遍歷是先選取一個(gè)頂點(diǎn),將它移出隊(duì)列時(shí)把它所有鄰接點(diǎn)入隊(duì),重復(fù)該操作
void BFS ( Vertex V )
{ visited [ V ] = true ; // 標(biāo)記為已訪問(wèn)
Enqueue ( V , Q ) ; // 壓入隊(duì)列中
while ( ! IsEmpty ( Q ) ) {
V = Dequeue ( Q ) ;
for ( V的每個(gè)鄰接點(diǎn) W )
if ( ! visited [ W ] ){
visited [ W ] = true ;
Enqueue ( W , Q ) ;
}
}
}
若有N個(gè)頂點(diǎn)、E條邊,時(shí)間復(fù)雜度是:
用鄰接表存儲(chǔ)圖,有 O ( N + E )
用鄰接矩陣存儲(chǔ)圖,有 O ( N的平方 )
為什么需要兩種遍歷??jī)煞N遍歷有不同特點(diǎn)
BFS:對(duì)于解決最短或最少問(wèn)題特別有效,而且尋找深度小,但缺點(diǎn)是內(nèi)存耗費(fèi)量大(需要開(kāi)大量的數(shù)組單元用來(lái)存儲(chǔ)狀態(tài))。DFS:對(duì)于解決遍歷和求所有問(wèn)題有效,對(duì)于問(wèn)題搜索深度小的時(shí)候處理速度迅速,然而在深度很大的情況下效率不高
圖不連通,怎么遍歷?深度遍歷和廣度遍歷都會(huì)丟棄孤立結(jié)點(diǎn)
連通:如果從V到W存在一條(無(wú)向)路徑,則稱(chēng)V和W是連通的
路徑:V到W的路徑是一系列頂點(diǎn){V,v1,v2,…,vn,W}集合,其中任一對(duì)相鄰的頂點(diǎn)間都有圖中的邊。路徑的長(zhǎng)度是路徑中的邊數(shù)(如果帶權(quán),則是所有邊的權(quán)重和)。如果V到W之間的所有頂點(diǎn)都不同,則稱(chēng)簡(jiǎn)單路徑。
回路:起點(diǎn)等于終點(diǎn)的路徑,帶有回路的就不是簡(jiǎn)單路徑
連通圖:圖中任意兩頂點(diǎn)均連通
連通分量:無(wú)向圖的極大連通子圖
極大頂點(diǎn)數(shù):再加1個(gè)頂點(diǎn)就不連通了
極大邊數(shù):包含子圖中所有頂點(diǎn)相連的所有邊
對(duì)于有向圖,有強(qiáng)連通和弱連通之分
強(qiáng)連通:有向圖中頂點(diǎn)V和W之間存在雙向路徑,則稱(chēng)V和W是強(qiáng)連通
弱連通:有向圖不是強(qiáng)連通圖,但是將該圖的路徑方向去掉后其變?yōu)檫B通圖,則稱(chēng)之為若連通
強(qiáng)連通圖:有向圖中任意兩頂點(diǎn)均強(qiáng)連通
每調(diào)用一次DFS(V),就把V所在的連通分量都遍歷了一遍。BFS也是一樣
void ListComponents ( Graph G ) // 所有分量列出來(lái)
{ for ( each V in G )
if ( ! visited [ V ] ) {
DFS ( V ) ; // or BFS ( V )
}
}
圖的簡(jiǎn)單應(yīng)用
-驗(yàn)證六度空間:
算法思路:
對(duì)每個(gè)節(jié)點(diǎn),進(jìn)行廣度優(yōu)先搜索
搜索過(guò)程中累計(jì)訪問(wèn)的節(jié)點(diǎn)數(shù)
需要記錄“層數(shù)”,僅僅計(jì)算6層以?xún)?nèi)的節(jié)點(diǎn)數(shù)
void SDS ( )
{
for ( each V in G ) {
count = BFS ( V ) ;
Output ( count / N ) ;
}
}
void BFS ( Vertex V )
{
visited [ V ] = true ; count = 1 ; // count 記錄訪問(wèn)的結(jié)點(diǎn)數(shù)量
level = 0 ; last = V ; // level指訪問(wèn)的層數(shù),last為訪問(wèn)該層的最后那個(gè)節(jié)點(diǎn)
Enqueue ( V , Q ) ;
while ( ! IsEmpty ( Q ) ) {
V = Dequeue ( Q ) ;
for ( V的每個(gè)鄰接點(diǎn) W )
if ( !visited [ W ] ) { //每一個(gè)V的鄰接點(diǎn)W進(jìn)隊(duì)列的時(shí)候,讓tail指向W
visited [ W ] = true ;
Enqueue ( W , Q ) ; count++ ;
tail = W ;
}
if ( V == last ) {
level ++ ; last = tail ; // 當(dāng)V是最后一個(gè)結(jié)點(diǎn)時(shí),層數(shù)加一
}
if ( level == 6 ) break ; // 層數(shù)為6 跳出
} return count ; // 輸出訪問(wèn)的總節(jié)點(diǎn)數(shù)
}
-最短路徑問(wèn)題
在網(wǎng)絡(luò)中,求兩個(gè)不同頂點(diǎn)之間的所有路徑中,邊的權(quán)值之和最小的那一條路徑,這條路徑就是兩點(diǎn)之間的最短路徑(Shortest Path)第一個(gè)頂點(diǎn)為源點(diǎn)(Source)最后一個(gè)頂點(diǎn)為終點(diǎn)(Destination)
問(wèn)題分類(lèi):
單源最短路徑問(wèn)題:從某固定源點(diǎn)出發(fā),求其到所有其他頂點(diǎn)的最短路徑
(有向)無(wú)權(quán)圖
(有向)有權(quán)圖
多源最短路徑問(wèn)題:求任意兩頂點(diǎn)間的最短路徑
無(wú)權(quán)圖的單源最短路徑算法
按照遞增(非遞減)的順序找出到各個(gè)頂點(diǎn)的最短路。BFS
初始化以下數(shù)組:
dist [ W ] = S到W的最短距離
dist [ S ] = 0
path [ W ] = S到W的路上經(jīng)過(guò)的某頂點(diǎn)
void Unweighted ( Vertex S )
{
Enqueue ( S , Q ) ; // 源點(diǎn)入隊(duì)
while ( ! IsEmpty ( Q ) ) {
V = Dequeue ( Q ) ;
for ( V的每個(gè)鄰接點(diǎn) W )
if ( list [ W ] == -1 ) {
dist [ W ] = dist [ V ] + 1 ;
path [ W ] = V ;
Enqueue ( W , Q ) ;
}
}
}
V個(gè)頂點(diǎn)E條邊,時(shí)間復(fù)雜度
T = O ( | V | + | E | )
有權(quán)圖的單源最短路算法(不一定是經(jīng)過(guò)頂點(diǎn)最少的路徑)圖中沒(méi)有負(fù)值圈
按照遞增(非遞減)的順序找出到各個(gè)頂點(diǎn)的最短路。Dijkstra算法
Dijkstra算法:令S={ 源點(diǎn) s+已經(jīng)確定了最短路徑的頂點(diǎn)Vi },對(duì)任一未收錄的頂點(diǎn)V,定義 dist [ V ]為S到V的最短路徑長(zhǎng)度,但該路徑僅經(jīng)過(guò)S中的頂點(diǎn)。即路徑 { S -> ( Vi 屬于 S ) -> V } 的最小長(zhǎng)度
若路徑是按照遞增(非遞減)的順序生成的,則:真正的最短路必須只經(jīng)過(guò)S中的頂點(diǎn),每次從未收錄的頂點(diǎn)中選一個(gè)dist最小的收錄,增加一個(gè)V進(jìn)入S,可能影響另一個(gè)W的dist值(此時(shí)V一定在路徑上且V有一條直接到W的邊)
void Dijkstra ( Vertex s )
{
while ( 1 ) {
V = 未收錄頂點(diǎn)中dist的最小值
if ( 這樣的V不存在 )
break ;
collected [ V ] = true ;
for ( V的每個(gè)鄰接點(diǎn)W )
if ( collected [ W ] == false )
if ( dist [ V ] + E小于 dist [ W ] ) {
dist [ W ] = dist [ V ] + E ;
path [ W ] = V ;
}
}
}
//不能解決有負(fù)邊的情況
如何得到未收錄頂點(diǎn)中dist的最小值并完成 dist [ W ] = dist [ V ] + E 操作
方法1:直接掃描所有未收錄頂點(diǎn)-O( | V | ),T = O ( | V | 的平方 + | E | );
方法2:將dist存在最小堆中-O( | E | log| V | ),更新 dist [ W ] 的值-O ( log| V | ),
T = O ( | V | log| V | + | E | log| V | ) = O( | E | log| V | )
多源最短路算法:
方法1:直接將單源最短路算法調(diào)用|V|遍,T = O ( |V|的立方 + | E | * | V | ),對(duì)于稀疏圖較優(yōu)
方法2:Floyd算法,T = O ( | V |的立方 ),對(duì)于稠密圖較優(yōu)
void Floyd ( )
{
for ( i = 0 ; i < N ; i ++ )
for ( j = 0 ; j < N ; j ++ ){
D[ i ][ j ] = G[ i ][ j ] ;
path[ i ][ j ] = -1 ;
}
for ( k = 0 ; k < N ; k ++ )
for ( i = 0 ; i < N ; i ++ )
for ( j = 0 ; j < N ; j ++ )
if ( D[ i ][ k ] + D[ k ][ j ] < D[ i ][ j ] ) {
D[ i ][ j ] = D[ i ][ k ] + D[ k ][ j ] ;
path[ i ][ j ] = k ;
}
}
T = O ( | V |的立方 )
-最小生成樹(shù)問(wèn)題
連通圖邊最少
什么是最小生成樹(shù):
首先是樹(shù),樹(shù)是一種特殊的圖,樹(shù)沒(méi)有回路,[ V ]個(gè)頂點(diǎn)一定有[ V ]-1條邊
生成樹(shù):包含全部頂點(diǎn),[ V ]-1條邊都在圖里,生成樹(shù)任意加一條邊都會(huì)生成回路
邊的權(quán)重和最小
貪心算法約束:只能用圖里有的邊,只能正好用掉 [ V ]-1條邊,不能有回路
Prime算法:讓一顆小樹(shù)長(zhǎng)大:從一頂點(diǎn)開(kāi)始,尋找一條與該樹(shù)相連的最小邊,在以連結(jié)的兩個(gè)頂點(diǎn)的樹(shù)為基礎(chǔ),再找與之相關(guān)的最小邊,加入的邊不能形成回路,依此類(lèi)推。有點(diǎn)像Dijkstra算法
void Prime ( )
{
MST = { s } ; // 初始化一棵最小生成樹(shù) 選擇根結(jié)點(diǎn)s
while ( 1 ) {
V = 未收錄頂點(diǎn)中 dist 的最小者;// dist定義為一個(gè)頂點(diǎn)到這棵生成樹(shù)的最小距離,dist [ V ] = E( v, w )或正無(wú)窮
if ( 這樣的V不存在 )
break ;
將V收錄進(jìn)MST;dist [ V ] = 0 ;
for ( V 的每個(gè)鄰接點(diǎn) W )
if ( dist [ W ] != 0 ) // W未被收錄
if ( E( v, w ) < dist [ W ] ) {
dist [ W ] = E( v, w ) ;
parent [ W ] = V ;
}
}
if ( MST中收錄的頂點(diǎn)不到 [ V ] 個(gè) ) // 有為連通的頂點(diǎn)
Error (“生成樹(shù)不存在”) ;
}
該算法適用于稠密圖
Kruskal算法-將森林合并成樹(shù),按照權(quán)重從小到大把邊收錄進(jìn)來(lái),注意不能構(gòu)成回路
void Kruskal ( graph G )
{
MST = { } ; // MST中收集的是邊,所以一開(kāi)始時(shí)空集
while ( MST 中不到 [ V ]-1條邊&& E中還有邊 ) {
從 E 中取一條權(quán)重最小的邊 E( v, w ) ; // 用最小堆存放邊
將 E( v, w )從 E 中刪除;
if ( E( v, w )不在 MST 中構(gòu)成回路 )
// 用并查集檢查是否構(gòu)成回路,每一個(gè)頂點(diǎn)是一個(gè)集合,收錄邊時(shí)兩棵樹(shù)變成一棵樹(shù),判斷新加入的邊與原來(lái)的是否屬于同一棵樹(shù)
將 E( v, w ) 加入MST;
else
徹底無(wú)視 E( v, w );
)
if ( MST 中不到 [ V ]-1條邊 )
Error ( “生成樹(shù)不存在” );
}
連通圖的最小生成樹(shù)不是唯一的,思考:
當(dāng)用Kruskal算法的思想加邊的時(shí)候出現(xiàn)(兩條權(quán)重相同并連接的兩棵樹(shù)相同的邊)的時(shí)候最小生成樹(shù)路徑不唯一
反之當(dāng)用Kruskal算法的思想加邊的時(shí)候沒(méi)出現(xiàn)(兩條權(quán)重相同并連接的兩棵樹(shù)相同的邊)的時(shí)候最小生成樹(shù)路徑唯一
或者說(shuō)決定路徑是否唯一的因素是使兩棵樹(shù)距離最小的邊是否只有一條,若有多條則不唯一