1. 程式人生 > >圖的遍歷 - 資料結構

圖的遍歷 - 資料結構

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               


概述

圖的遍歷是指從圖中的任一頂點出發,對圖中的所有頂點訪問一次且只訪問一次。圖的遍歷操作和樹的遍歷操作功能相似。圖的遍歷是圖的一種基本操作,圖的其它演算法如求解圖的連通性問題,拓撲排序,求關鍵路徑等都是建立在遍歷演算法的基礎之上。

由於圖結構本身的複雜性,所以圖的遍歷操作也較複雜,主要表現在以下四個方面:
① 在圖結構中,沒有一個“自然”的首結點,圖中任意一個頂點都可作為第一個被訪問的結點。
② 在非連通圖中,從一個頂點出發,只能夠訪問它所在的連通分量上的所有頂點,因此,還需考慮如何選取下一個出發點以訪問圖中其餘的連通分量。
③ 在圖結構中,如果有迴路存在,那麼一個頂點被訪問之後,有可能沿迴路又回到該頂點。

④ 在圖結構中,一個頂點可以和其它多個頂點相連,當這樣的頂點訪問過後,存在如何選取下一個要訪問的頂點的問題。

圖的遍歷通常有深度優先搜尋和廣度優先搜尋兩種方式,他們對無向圖和有向圖都適用。

1.深度優先搜尋

深度優先搜尋(Depth_Fisrst Search)遍歷類似於樹的先根遍歷,是樹的先根遍歷的推廣。

 假設初始狀態是圖中所有頂點未曾被訪問,則深度優先搜尋可從圖中某個頂點發v 出發,訪問此頂點,然後依次從v 的未被訪問的鄰接點出發深度優先遍歷圖,直至圖中所有和v 有路徑相通的頂點都被訪問到;若此時圖中尚有頂點未被訪問,則另選圖中一個未曾被訪問的頂點作起始點,重複上述過程,直至圖中所有頂點都被訪問到為止。

以如下圖的無向圖G5為例,進行圖的深度優先搜尋:


G5

搜尋過程:



假設從頂點v1 出發進行搜尋,在訪問了頂點v1 之後,選擇鄰接點v2。因為v2 未曾訪問,則從v2 出發進行搜尋。依次類推,接著從v4 、v8 、v5 出發進行搜尋。在訪問了v5 之後,由於v5 的鄰接點都已被訪問,則搜尋回到v8。由於同樣的理由,搜尋繼續回到v4,v2 直至v1,此時由於v1 的另一個鄰接點未被訪問,則搜尋又從v1 到v3,再繼續進行下去由此,得到的頂點訪問序列為:



顯然,這是一個遞迴的過程。為了在遍歷過程中便於區分頂點是否已被訪問,需附設訪問標誌陣列visited[0:n-1], ,其初值為FALSE ,一旦某個頂點被訪問,則其相應的分量置為TRUE。
1)鄰接矩陣的儲存方式實現:

// stdafx.h : include file for standard system include files,// or project specific include files that are used frequently, but// are changed infrequently//#pragma once#include "targetver.h"#include <stdio.h>  #include "stdlib.h"#include <iostream>using namespace std;//巨集定義    #define TRUE   1    #define FALSE   0   #define NULL 0#define OK    1    #define ERROR   0  #define INFEASIBLE -1    #define OVERFLOW -2  #define INFINITY   INT_MAX#define MAX_VERTEX_NUM 30typedef int Status   ;typedef int ElemType ;typedef int VrType  ;typedef char VertexType  ;/************************************************************************//* 陣列表示:鄰接矩陣資料結構*//************************************************************************/typedef struct ArcCell{ VrType adj;       //頂點關係型別,對無權圖,0/1表示是否相鄰,有權圖表示權值 ArcCell  *info;      //弧相關資訊的指標}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];typedef struct{ VertexType vexs[MAX_VERTEX_NUM];   //頂點向量 AdjMatrix arcs;                    //鄰接矩陣 int vexnum,arcnum;                 //圖的當前頂點數和弧數}MGraph;


// Test.cpp : Defines the entry point for the console application.  //  #include "stdafx.h" bool visited[MAX_VERTEX_NUM];  //訪問標識Status (*VisitFunc) (int v);   //函式變數/************************************************************************//*   確定頂點v在圖G的位置*//************************************************************************/int LocateVex(MGraph G,VertexType v)for(int i = 0; i<G.vexnum; ++i) {  if(G.vexs[i] == v) return i;//找到 } return -1;//不存在}/************************************************************************//*    *//************************************************************************/int FirstAdjVex(MGraph G,int v)int i ; for(i = 0; i<G.vexnum; i++)  if( G.arcs[v][i].adj ) return i; if(i == (G.vexnum  -1)) return -1return -1; }int NextAdjVex(MGraph G,int v,int w)int i; for( i = w+1; i<G.vexnum; i++)//+1  if(G.arcs[v][i].adj) return i; if(i == (G.vexnum  -1)) return -1return -1;}/************************************************************************//* 鄰接矩陣的無向圖的建立: 註釋的程式碼可以動態生成圖。*//************************************************************************/void CreatUDG(MGraph &G)cout<<"建立鄰接矩陣的無向圖:"<<endlint i,j,k,w; //G5的儲存: G.arcnum = 8; G.vexnum = 9for(i=0;i<G.vexnum;++i)  for(j=0;j<G.vexnum;++j) {   G.arcs[i][j].adj=0;   G.arcs[i][j].info=NULL;  } G.vexs[0] = '1'; G.vexs[1] = '2'; G.vexs[2] = '3'; G.vexs[3] = '4'; G.vexs[4] = '5'; G.vexs[5] = '6'; G.vexs[6] = '7'; G.vexs[7] = '8'; G.arcs[0][1].adj = 1; G.arcs[0][1].info = NULL; G.arcs[1][0].adj = 1; G.arcs[1][0].info = NULL; G.arcs[1][3].adj = 1; G.arcs[1][3].info = NULL; G.arcs[3][1].adj = 1; G.arcs[3][1].info = NULL; G.arcs[3][7].adj = 1; G.arcs[3][7].info = NULL; G.arcs[7][3].adj = 1; G.arcs[7][3].info = NULL; G.arcs[7][4].adj = 1; G.arcs[7][4].info = NULL; G.arcs[4][7].adj = 1; G.arcs[4][7].info = NULL; G.arcs[4][1].adj = 1; G.arcs[4][1].info = NULL; G.arcs[1][4].adj = 1; G.arcs[1][4].info = NULL; G.arcs[0][2].adj = 1; G.arcs[0][2].info = NULL; G.arcs[2][0].adj = 1; G.arcs[2][0].info = NULL; G.arcs[2][5].adj = 1; G.arcs[2][5].info = NULL; G.arcs[5][2].adj = 1; G.arcs[5][2].info = NULL; G.arcs[5][6].adj = 1; G.arcs[5][6].info = NULL; G.arcs[6][5].adj = 1; G.arcs[6][5].info = NULL; G.arcs[6][2].adj = 1; G.arcs[6][2].info = NULL; G.arcs[2][6].adj = 1; G.arcs[2][6].info = NULLreturn/* char v1,v2; cout<<"請輸入無向圖頂點個數和邊數:"<<endl; cin>>G.vexnum>>G.arcnum; cout<<"請輸入"<<G.vexnum<<"個頂點的值:"<<endl; for(i=0;i<G.vexnum;++i) cin>>G.vexs[i]; for(i=0;i<G.vexnum;++i)  for(j=0;j<G.vexnum;++j) {   G.arcs[i][j].adj=0;   G.arcs[i][j].info=NULL;  }  for( k=1;k<=G.arcnum;++k){   cout<<"請輸入第"<<k<<"條邊的兩個頂點值和它們的權重:"<<endl;   cin>>v1>>v2>>w;   i = LocateVex(G,v1);   j=LocateVex(G,v2);    G.arcs[i][j].adj=w;   G.arcs[j][i]=G.arcs[i][j];  }  */}/************************************************************************//* 有向圖鄰接矩陣的建立*//************************************************************************/void CreatDG(MGraph &G)int i,j,k,w; char v1,v2; G.arcnum = 8; G.vexnum = 9cout<<"請輸入有向圖頂點個數和邊數:"cin>> G.vexnum>> G.arcnum; cout<<"請輸入"<<G.vexnum<<"個頂點的值:"<<endlfor(i=0;i<G.vexnum;++i) cin>>G.vexs[i]; for(i=0;i<G.vexnum;++i)  for(j=0;j<G.vexnum;++j) {   G.arcs[i][j].adj = 0;   G.arcs[i][j].info = NULL;  }  for( k=1;k<=G.arcnum;++k){   cout<<"請輸入第"<<k<<"條邊的兩個頂點值和它們的權重:"<<endl;   cin>>v1>>v2>>w;   i= LocateVex(G,v1);   j = LocateVex(G,v2);    G.arcs[i][j].adj = w;  }}void visitVex(MGraph G, int v)cout<<G.vexs[v]<<" ";}/************************************************************************//*  以V為出發點對圖G 進行遞迴地DFS 搜尋*//************************************************************************/void DFS(MGraph G,int v){ visited[v] = true; visitVex( G,  v); //訪問第v 個頂點 for(int w = FirstAdjVex(G,v); w>=0; w = NextAdjVex(G,v,w)){  if(!visited[w]) DFS(G,w); //w未訪問過,遞迴DFS搜尋 }}/************************************************************************//*     無向圖的深度遍歷       *//************************************************************************/void DFSTraverse(MGraph G){// int v; for( v = 0; v < G.vexnum; ++v) visited[v] = falsefor( v = 0; v < G.vexnum; )   if(!visited[v]) DFS( G, v); //v未訪問過,從vi開始DFS搜尋  ++v;//不要像書上寫的那樣,++v放到for語句,這樣會導致多出一次訪問}void printMGraph(MGraph G)cout<<"鄰接矩陣已經建立,鄰接矩陣為:"<<endlfor(int i=0;i<G.vexnum;i++){  for(int j=0;j<G.vexnum;j++)   cout<<G.arcs[i][j].adj<<" ";  cout<<endl; }}void main(){  MGraph G; CreatUDG(G); printMGraph(G); cout<<"無向圖鄰接矩陣的深度遍歷結果:"<<endl; DFSTraverse(G);}

2) 鄰接表的表示實現方式

// stdafx.h : include file for standard system include files,// or project specific include files that are used frequently, but// are changed infrequently//#pragma once#include "targetver.h"#include <stdio.h>  #include "stdlib.h"#include <iostream>using namespace std;//巨集定義    #define TRUE   1    #define FALSE   0   #define NULL 0#define OK    1    #define ERROR   0  #define INFEASIBLE -1    #define OVERFLOW -2  #define INFINITY   INT_MAX#define MAX_VERTEX_NUM 30typedef int Status   ;typedef int ElemType ;typedef int VrType  ;typedef char VertexType  ;/************************************************************************//*  鄰接表示的圖資料結構*//************************************************************************///定義邊結點,即表節點typedef struct ArcNode { int adjvex;          //弧所指的頂點位置 ArcNode *nextarc;  //指向下一條弧的指標}ArcNode;//定義頂點節點,即頭節點typedef struct VNode  { VertexType data;  //頂點資訊 ArcNode *firstarc;  //指向第一條依附該頂點的弧的指標}VNode,AdjList[MAX_VERTEX_NUM];//定義無向圖   typedef struct                      { AdjList vertices; int vexnum,arcnum;   //圖的當前頂點數和弧數}ALGraph;

// Test.cpp : Defines the entry point for the console application.  //  #include "stdafx.h" bool visited[MAX_VERTEX_NUM];  //訪問標識Status (*VisitFunc) (int v);   //函式變數/************************************************************************//* 在無向圖中新增以m,n為頂點的邊*//************************************************************************/void ArcAdd(ALGraph &G,int m,int n){ ArcNode *p,*h,*q; p = new ArcNode; p->adjvex = m; p->nextarc = NULL; h = q = G.vertices[n].firstarc; if(q == NULL)  G.vertices[n].firstarc = p; else {   if((p->adjvex)>(q->adjvex)){    p->nextarc = q;   G.vertices[n].firstarc = p;  }  else {    while( G.vertices[n].firstarc != NULL && q->nextarc != NULL && (p->adjvex)<(q->adjvex)){ //使鄰接表中邊的資料按大到小排列。      h = q;    q = q->nextarc;   }   if(q->nextarc == NULL&&(p->adjvex)<(q->adjvex)){      q->nextarc = p;   }   else {      p->nextarc = q;    h->nextarc = p;   }  } }}/************************************************************************//*建立無向圖*//************************************************************************/void CreateDG(ALGraph &G){   cout<<"請輸入頂點個數和邊數:"<<endlcin>> G.vexnum>> G.arcnum; cout<<"請輸入頂點值:"<<endlfor(int i= 1; i<= G.vexnum; i++) {  char t;  cin>>t;  G.vertices[i].data = t;  G.vertices[i].firstarc = NULL; } int m, n; for(int k = 1; k<=G.arcnum; k++){  cout<<"請輸入第"<<k<<"條邊的兩個頂點:"<<endl;  cin>>m>>n;  if(m<= G.vexnum && n <= G.vexnum && m>0 && n>0){   ArcAdd(G, m, n);   ArcAdd(G, n, m);  }  else  cout<<"ERROR."<<endl;  }}/************************************************************************//* 列印鄰接表的無向圖      *//************************************************************************/void PrintGraph(ALGraph G)  cout<<"無向圖的建立完成,該圖的鄰接表表示為:"<<endl; ArcNode *p; for(int i=1; i<=G.vexnum; i++) {  if(G.vertices[i].firstarc == NULL)   cout<<i<<G.vertices[i].data<<"-->NULL"<<endl;  else   {   p = G.vertices[i].firstarc;   cout<<i<<G.vertices[i].data<<"-->";   while(p->nextarc!=NULL)   {    cout<<p->adjvex<<"-->";    p = p->nextarc;   }   cout<<p->adjvex<<"-->NULL"<<endl;  } }}/************************************************************************//*     返回v的第一個鄰接頂點。若頂點在G中沒有鄰接表頂點,則返回“空”。   *//************************************************************************/int FirstAdjVex(ALGraph G,int v){  if(G.vertices[v].firstarc)  return G.vertices[v].firstarc->adjvex; else  return NULL;}/************************************************************************//*     返回v的(相對於w的)下一個鄰接頂點。若w是v的最後一個鄰接點,則返回“回”。*//************************************************************************/int NextAdjVex(ALGraph G,int v,int w)   { ArcNode *p; if(G.vertices[v].firstarc==NULL)  return NULLelse {  p = G.vertices[v].firstarc;  while(p->adjvex!=w) p = p->nextarc;  if(p->nextarc == NULL) return NULL;  else  return p->nextarc->adjvex; }}void visitVex(ALGraph G, int v)cout<<G.vertices[v].data<<" ";}/************************************************************************//*     無向圖的深度遍歷       *//************************************************************************///從第v個頂點出發遞迴地深度優先遍歷圖Gvoid DFS(ALGraph G,int v){ visited[v] = true; visitVex(G, v); for(int w = FirstAdjVex(G,v);w >= 1; w = NextAdjVex(G,v,w))  if(!visited[w]) DFS(G,w);}//對圖G作深度優先遍歷void DFSTraverse(ALGraph G){  for(int v = 1; v <= G.vexnum; v++) visited[v]=falsefor(int m = 1; m <= G.vexnum; m++)  if(!visited[m]) DFS(G,m);}void main(){ ALGraph G; CreateDG(G); PrintGraph(G); DFSTraverse(G);}


分析上述演算法,在遍歷時,對圖中每個頂點至多呼叫一次DFS 函式,因為一旦某個頂點被標誌成已被訪問,就不再從它出發進行搜尋。因此,遍歷圖的過程實質上是對每個頂點查詢其鄰接點的過程。其耗費的時間則取決於所採用的儲存結構。當用二維陣列表示鄰接矩陣圖的儲存結構時,查詢每個頂點的鄰接點所需時間為O(n2) ,其中n 為圖中頂點數。而當以鄰接表作圖的儲存結構時,找鄰接點所需時間為O(e),其中e 為無向圖中邊的數或有向圖中弧的數。由此,當以鄰接表作儲存結構時,深度優先搜尋遍歷圖的時間複雜度為O(n+e) 。


2.廣度優先搜尋

廣度優先搜尋(Breadth_First Search) 遍歷類似於樹的按層次遍歷的過程。

假設從圖中某頂點v 出發,在訪問了v 之後依次訪問v 的各個未曾訪問過和鄰接點,然後分別從這些鄰接點出發依次訪問它們的鄰接點,並使“先被訪問的頂點的鄰接點”先於“後被訪問的頂點的鄰接點”被訪問,直至圖中所有已被訪問的頂點的鄰接點都被訪問到。若此時圖中尚有頂點未被訪問,則另選圖中一個未曾被訪問的頂點作起始點,重複上述過程,直至圖中所有頂點都被訪問到為止。換句話說,廣度優先搜尋遍歷圖的過程中以v 為起始點,由近至遠,依次訪問和v 有路徑相通且路徑長度為1,2,…的頂點。

對圖如下圖所示無向圖G5 進行廣度優先搜尋遍歷:



廣度搜索過程:


首先訪問v1 和v1 的鄰接點v2 和v3,然後依次訪問v2 的鄰接點v4 和v5 及v3 的鄰接點v6 和v7,最後訪問v4 的鄰接點v8。由於這些頂點的鄰接點均已被訪問,並且圖中所有頂點都被訪問,由些完成了圖的遍歷。得到的頂點訪問序列為:


v1→v2 →v3 →v4→ v5→ v6→ v7 →v8
和深度優先搜尋類似,在遍歷的過程中也需要一個訪問標誌陣列。並且,為了順次訪問路徑長度為2、3、…的頂點,需附設佇列以儲存已被訪問的路徑長度為1、2、… 的頂點。

實現:

// stdafx.h : include file for standard system include files,// or project specific include files that are used frequently, but// are changed infrequently//#pragma once#include <stdio.h>  #include "stdlib.h"

// func.h :#pragma once#include <iostream>using namespace std;//巨集定義    #define TRUE   1    #define FALSE   0   #define NULL 0#define OK    1    #define ERROR   0  #define INFEASIBLE -1    #define OVERFLOW -2  #define INFINITY   INT_MAX#define MAX_VERTEX_NUM 30typedef int Status   ;typedef int ElemType ;typedef int VrType  ;typedef char VertexType  ;/************************************************************************//*  鄰接表示的圖資料結構*//************************************************************************///定義邊結點typedef struct ArcNode { int adjvex;    //弧所指的頂點位置 ArcNode *nextarc;  //指向下一條弧的指標}ArcNode;//定義頂點結點typedef struct VNode  { VertexType data;  //頂點資訊 ArcNode *firstarc;  //指向第一條依附該頂點的弧的指標}VNode,AdjList[MAX_VERTEX_NUM];//定義無向圖   typedef struct                      { AdjList vertices; int vexnum,arcnum;   //圖的當前頂點數和弧數}ALGraph;/************************************************************************//* 需要                                                                     *//************************************************************************/typedef struct node //定義結點{ char data; node *next;}*Link;typedef struct //定義連結串列{ Link head,tail; int len;}Queue;/************************************************************************//* 構造一個帶頭結點和尾結點的空的線性連結串列佇列Q*//************************************************************************/Status InitQueue(Queue &Q){ Q.head = new node; Q.head->next = Q.tail = new node; Q.tail->next = NULL; Q.len = 0return 0;}/************************************************************************//*  //線上性連結串列的佇列L的結尾新增一個結點*//************************************************************************/ void EnQueue(Queue &Q,int e){ Link q = new node; Q.tail->next = q; Q.tail->data = e; Q.tail = q; Q.tail->next = NULL; Q.len++;}/************************************************************************//* 出列,並將出列的元素值用e返回*//************************************************************************/void DeleteQueue(Queue &Q,int &e)if(Q.head->next == Q.tail) {  cout<<"佇列為空"<<endl;  e = NULL; } else {  Link p,q;  p = Q.head->next;  q = p->next;  Q.head->next = q;  e = p->data;  delete p;  Q.len--; }}

// Test.cpp : Defines the entry point for the console application.  //  #include "stdafx.h" #include "func.h"bool visited[MAX_VERTEX_NUM];  //訪問標識Status (*VisitFunc) (int v);   //函式變數/************************************************************************//* 在無向圖中新增以m,n為頂點的邊*//************************************************************************/void ArcAdd(ALGraph &G,int m,int n){ ArcNode *p,*h,*q; p = new ArcNode; p->adjvex = m; p->nextarc = NULL; h = q = G.vertices[n].firstarc; if(q == NULL)  G.vertices[n].firstarc = p; else {   if((p->adjvex)>(q->adjvex)){    p->nextarc = q;   G.vertices[n].firstarc = p;  }  else {    while( G.vertices[n].firstarc != NULL && q->nextarc != NULL && (p->adjvex)<(q->adjvex)){     //使鄰接表中邊的資料按大到小排列。      h = q;    q = q->nextarc;   }   if(q->nextarc == NULL&&(p->adjvex)<(q->adjvex)){      q->nextarc = p;   }   else {      p->nextarc = q;    h->nextarc = p;   }  } }}/************************************************************************//*建立無向圖*//************************************************************************/void CreateDG(ALGraph &G){   cout<<"請輸入頂點個數和邊數:"<<endlcin>> G.vexnum>> G.arcnum; cout<<"請輸入頂點值:"<<endlfor(int i= 1; i<= G.vexnum; i++) {  char t;  cin>>t;  G.vertices[i].data = t;  G.vertices[i].firstarc = NULL; } int m, n; for(int k = 1; k<=G.arcnum; k++){  cout<<"請輸入第"<<k<<"條邊的兩個頂點:"<<endl;  cin>>m>>n;  if(m<= G.vexnum && n <= G.vexnum && m>0 && n>0){   ArcAdd(G, m, n);   ArcAdd(G, n, m);  }  else  cout<<"ERROR."<<endl;  }}/************************************************************************//* 列印鄰接表的無向圖      *//************************************************************************/void PrintGraph(ALGraph G)  cout<<"無向圖的建立完成,該圖的鄰接表表示為:"<<endl; ArcNode *p; for(int i=1; i<=G.vexnum; i++) {  if(G.vertices[i].firstarc == NULL)   cout<<i<<G.vertices[i].data<<"-->NULL"<<endl;  else   {   p = G.vertices[i].firstarc;   cout<<i<<G.vertices[i].data<<"-->";   while(p->nextarc!=NULL)   {    cout<<p->adjvex<<"-->";    p = p->nextarc;   }   cout<<p->adjvex<<"-->NULL"<<endl;  } }}/************************************************************************//*     返回v的第一個鄰接頂點。若頂點在G中沒有鄰接表頂點,則返回“空”。   *//************************************************************************/int FirstAdjVex(ALGraph G,int v){  if(G.vertices[v].firstarc)  return G.vertices[v].firstarc->adjvex; else  return NULL;}/************************************************************************//*     返回v的(相對於w的)下一個鄰接頂點。若w是v的最後一個鄰接點,則返回“回”。*//************************************************************************/int NextAdjVex(ALGraph G,int v,int w)   { ArcNode *p; if(G.vertices[v].firstarc==NULL)  return NULLelse {  p = G.vertices[v].firstarc;  while(p->adjvex!=w) p = p->nextarc;  if(p->nextarc == NULL) return NULL;  else  return p->nextarc->adjvex; }}void visitVex(ALGraph G, int v)cout<<G.vertices[v].data<<" ";}/************************************************************************//*     廣度優先遍歷圖G*//************************************************************************/void BFSTraverse(ALGraph G){ Queue Q; int u; for(int m=1; m<= G.vexnum; m++) visited[m] = false; InitQueue(Q);//藉助輔助佇列。 for(int v=1;v<=G.vexnum;v++)  if(!visited[v]) {   visited[v]=true;   visitVex(G,v);   EnQueue(Q,v);   while(Q.len!=0)   {    DeleteQueue(Q,u);    for(int w=FirstAdjVex(G,u);w>=1;w=NextAdjVex(G,u,w))     if(!visited[w])     {      visited[w]=true;      visitVex(G,v);      EnQueue(Q,w);     }   }  }  cout<<endl;}void main(){ ALGraph G; CreateDG(G); PrintGraph(G); cout<<"廣度優先搜尋的結果為:"<<endl; BFSTraverse(G);}


分析上述演算法,每個頂點至多進一次佇列。遍歷圖的過程實質是通過邊或弧找鄰接點的過程,因此廣度優先搜尋遍歷圖的時間複雜度和深度優先搜尋遍歷相同,兩者不同之處僅僅在於對頂點訪問的順序不同。

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述