演算法精解----17、圖的應用
阿新 • • 發佈:2019-02-19
知識小結:
(1)連結串列可以動態擴充套件和收縮,這是較陣列的優點
(2)網際網路建模,關節點代表單點故障源,如果該點出錯則系統無法通訊。因此在設計保持長時間連線的大型網路架構時,很重要的一點就是保證網路中沒有關節點。
(3)有向圖很適合狀態機建模
(4)超圖:包含超邊,即可以連線任意數量頂點的邊
(5)多重圖:允許在相同的兩個頂點間有多條邊存在
(6)鄰接矩陣表示法:V*V階矩陣,V 頂點個數,如果頂點u,v之間存在一條邊,則在矩陣[u,v]處設定一個標誌
應用:
1、計算網格跳數-採用廣度優先搜尋確定節點之間的最小跳數
2、廣度優先搜尋程式碼
資料結構
這裡的BfsVertex,即AdjList中*vertex指向的內容。包含節點資料、顏色、跳數。
typedef struct BfsVertex_{
void *data;
VertexColor color;
int hops;
}BfsVertex;
//start:指定起始點,hops:返回起始點能夠到達的所有點
int bfs(Graph *graph, BfsVertex *start, List *hops)
{
Queue queue;
AdjList *adjlist, *clr_adjlist;
BfsVertex *clr_vertex, *adj_vertex;
LIST_ELEMENT *element, * member;
//找到起點塗灰,跳數為0,其餘圖白,跳數為-1
for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
{
clr_vertex = ((AdjList *)element->data)->vertex;
if(graph->match(clr_vertex, start))
{
clr_vertex->color = gray;
clr_vertex-> hops = 0;
}
else
{
clr_vertex->color = white;
clr_vertex->hops = -1;
}
}
queue_init(&queue,NULL);
//找到與起點對應的資訊存於clr_adjlist
if(graph_adjlist(graph, start, &clr_adjlist) != 0)
{
queue_destroy(&queue);
return -1;
}
//起點入佇列
if(queue_enqueue(&queue, clr_adjlist) != 0)
{
queue_destroy(&queue);
return -1;
}
while(queue_size(&queue) > 0)
{
//起點出佇列,把起點的鄰接點塗灰,hops加1如佇列
//之前已經塗灰的不會如佇列,這就避免節點重複
adjlist = queue_peek(&queue);
for(member = list_head(&adjlist->adjacent); member != NULL; member = list_next(element))
{
adj_vertex = member->data;
if(graph_adjlist(graph, adj_vertex, &clr_adjlist) != 0)
{
queue_destroy(&queue);
return -1;
}
clr_vertex = clr_adjlist->vertex;
if(clr_vertex->color == white)
{
clr_vertex->color = gray;
clr_vertex->hops = ((BfsVertex *)(adjlist->vertex)->hops + 1);
if(queue_enqueue(&queue, clr_adjlist) != 0)
{
queue_destroy(&queue);
return -1;
}
}
}
//出佇列一個二級節點,塗黑,以這個圖黑的節點取代起始節點的地位,再次尋找它的鄰接點入佇列(先進先出)
//然後再出第二個二級節點,塗黑重複
if(queue_dequeue(&queue, (void **)&adjlist) == 0)
{
((BfsVertex *)adjlist->vertex)->color = black;
}
else
{
queue_destroy(&queue);
return -1;
}
}
queue_destroy(&queue);
list_init(hops, NULL);
//把跳數不為-1的點(由起始點能到達的點)放到連結串列hops中
for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
{
clr_vertex = ((AdjList *)element->data)->vertex;
if(clr_vertex->hops != -1)
{
if(list_ins_next(hops, list_tail(hops), clr_vertex) != 0)
{
list_destroy(hops);
return -1;
}
}
}
return 0;
}
3、拓撲排序-深度優先搜尋
某些階段必須在其他階段之前完成,採用優先順序圖建模。在優先順序圖中,頂點代表任務,邊代表任務之間的依賴關係。
遞迴到最深處的點,塗黑、壓棧
(1)資料結構
typedef struct DfsVertex_{
void *data;
VertexColor color;
}DfsVertex;
(2)遞迴深度搜索
//ordered:存放由最深處點開始的鏈路
int dfs_main(Graph *graph, AdjList *adjlist, List *ordered)
{
AdjList *clr_adjlist;
BfsVertex *clr_vertex, *adj_vertex;
LIST_ELEMENT *member;
((DfsVertex *)adjlist->vertex)->color = gray;
//遍歷當前節點的鄰節點
for(member = list_head(&adjlist->adjacent); member != NULL; member = list_next(member))
{
adj_vertex = list_data(member);
if(graph_adjlist(graph, adj_vertex, &clr_adjlist) != 0)
return -1;
clr_vertex = clr_adjlist->vertex;
//往深處遞迴
if(clr_vertex->color == white)
{
if(dfs_main(graph, clr_adjlist, ordered) != 0)
return -1;
}
}
//第一個塗黑並壓棧的其實是最深處的點
((DfsVertex *)adjlist->vertex)->color = black;
if(list_ins_next(ordered, NULL, (DfsVertex *)adjlist->vertex) != 0)
return -1;
return 0;
}
(3)初始化,對每個白點執行深度搜索
int dfs(Graph *graph, List *ordered)
{
DfsVertex *vertex;
LIST_ELEMENT *elememt;
for(elememt = list_head(graph->adjlists); elememt != NULL; elememt = list_next(elememt))
{
vertex = ((AdjList *)list_data(elememt))->vertex;
vertex->color = white;
}
list_init(ordered, NULL);
for(elememt = list_head(graph->adjlists); elememt != NULL; elememt = list_next(elememt))
{
vertex = ((AdjList *)list_data(elememt))->vertex;
if(vertex->color == white)
{
if(dfs_main(graph, (AdjList *)list_data(elememt), ordered) != 0)
{
list_destroy(ordered);
return -1;
}
}
}
return 0;
}