無向圖G是一個徹底圖。html
路徑:鏈接圖中兩個頂點的邊的序列,能夠由多條邊組成。java
無向圖中的路徑是雙向的。ios
連通圖:無向圖中 任意兩個頂點間都有路徑。例如:git
徹底圖必定是連通圖,連通圖不必定是徹底圖。算法
其中,(b),(c)是(a)的子圖。數組
有向圖:頂點之間有序鏈接,邊是頂點的有序對。網絡
邊(A,B)和(B,A)方向不一樣。數據結構
有向圖中的有序對經常使用序偶表示,例如:函數
上圖中路徑 V00→V22→V33 是從V00到V33的路徑,可是反過來再也不成立。學習
上圖中左圖爲聯通圖,右圖不聯通,由於從任何頂點到頂點1都沒有路徑。
有向樹是一個有向圖,其中指定一個元素爲根,則具備下列特性:
任何頂點到根都沒有鏈接。
到達每一個非根元素的鏈接都只有一個。
從根到每一個頂點都有路徑。
網絡能夠是無向的也能夠是有向的。
在深度優先遍歷中,須要兩個棧,這裏能夠看出深度優先遍歷帶有遞歸的性質。一個棧用來輔助遍歷,即用來保存遍歷過程當中裏面的頂點,另外一個棧用來保存遍歷的順序。之因此另外須要一個棧來保存遍歷的順序的緣由 與 廣度優先遍歷 中須要用另外一個隊列來保存 遍歷順序 的緣由相同。當深度優先遍歷到某個頂點時,若該頂點的全部鄰接點均已經被訪問,則發生回溯,即返回去遍歷 該頂點 的 前驅頂點 的 未被訪問的某個鄰接點。
圖的深度優先遍歷與廣度優先遍歷的惟一不一樣是,他使用的是棧而不是隊列來管理遍歷。
一般構造最小生成樹的算法有兩種:
它是從點的方面考慮構建一顆MST,大體思想是:設圖G頂點集合爲U,首先任意選擇圖G中的一點做爲起始點a,將該點加入集合V,再從集合U-V中找到另外一點b使得點b到V中任意一點的權值最小,此時將b點也加入集合V;以此類推,如今的集合V={a,b},再從集合U-V中找到另外一點c使得點c到V中任意一點的權值最小,此時將c點加入集合V,直至全部頂點所有被加入V,此時就構建出了一顆MST。由於有N個頂點,因此該MST就有N-1條邊,每一次向集合V中加入一個點,就意味着找到一條MST的邊。
假設 WN=(V,{E}) 是一個含有 n 個頂點的連通網,則按照克魯斯卡爾算法構造最小生成樹的過程爲:先構造一個只含 n 個頂點,而邊集爲空的子圖,若將該子圖中各個頂點當作是各棵樹上的根結點,則它是一個含有 n 棵樹的一個森林。以後,從網的邊集 E 中選取一條權值最小的邊,若該條邊的兩個頂點分屬不一樣的樹,則將其加入子圖,也就是說,將這兩個頂點分別所在的兩棵樹合成一棵樹;反之,若該條邊的兩個頂點已落在同一棵樹上,則不可取,而應該取下一條權值最小的邊再試之。依次類推,直至森林中只有一棵樹,也即子圖中含有 n-1條邊爲止。
相對應的鄰接矩陣表示以下:
#include<iostream> #include<string> #define maxSize 10 using namespace std; //在此聲明 不用template 模板 class Graph{ public: Graph(string str[],int vertex,int arc1); ~Graph(){ //因爲沒有動態申請內存 沒法對改內存進行釋放(在此處作聲明) cout<<"析構函數調用成功!"; }; void DFS(int v); //深度優先遍歷 void FdgDFS(int v); //非遞歸的深度優先遍歷 void BFS(int v); //廣度優先遍歷 private: int verNum; int arcNum; string verName[maxSize]; //對於鄰接矩陣構造無向圖確定少不了鄰接矩陣 即二維數組 int arc[maxSize][maxSize]; //遍歷時,對於沒有訪問過的頂點,須要創建flag 標識 int visited[maxSize]={0}; }; Graph::Graph(string str[],int vertex,int arc1){ verNum=vertex; //頂點的賦值 for(int i=0;i<verNum;i++){ verName[i]=str[i]; } for(int i=0;i<verNum;i++) //初始化鄰接矩陣 for(int j=0;j<verNum;j++) arc[i][j]=arc[j][i]=0; //對邊進行構造 輸入依附於邊的鄰接點的下標 arcNum=arc1; for(int k=0;k<arcNum;k++){ int i=0; int j=0; cout<<"請輸入依附於邊的鄰接點下標 "<<endl; cin>>i>>j; //無向圖的鄰接矩陣是對稱的 arc[i][j]=arc[j][i]=1; } } //遞歸的深度遍歷 void Graph::DFS(int v){ cout<<verName[v]; visited[v]=1; for(int j=0;j<verNum;j++){ if(arc[v][j]==1 && visited[j]==0) DFS(j); } } //非遞歸的深度優先遍歷 void Graph::FdgDFS(int v){ int Stack[maxSize]; int top=-1; cout<<verName[v]; visited[v]=1; Stack[++top]=v; while(top!=-1){ v=Stack[top]; for(int j=0;j<verNum;j++){ if(arc[v][j]==1 && visited[j]==0){ cout<<verName[j]; visited[j]=1; Stack[++top]=j; break; } if(j==verNum-1) top--; } } } //廣度優先遍歷 void Graph::BFS(int v){ //定義隊列進行遍歷 int Queue[maxSize]; int front=-1; int rear=-1; cout<<verName[v]; visited[v]=1; Queue[++rear]=v; while(front!=rear){ //出隊 v=Queue[++front]; for(int j=0;j<verNum;j++) if(arc[v][j]==1 && visited[j]==0){ cout<<verName[j]; visited[j]=1; //入隊 Queue[++rear]=j; } } } int main(){ string mystr[4]={"v0","v1","v2","v3"}; Graph myGraph(mystr,4,4); //深度優先遍歷 myGraph.DFS(2); //非遞歸深度優先遍歷 myGraph.FdgDFS(2); //廣度優先遍歷 myGraph.BFS(2); return 0; }
我看到一個大佬的博客解決了這個問題,並且我閱讀以後也有了比較深入的理解
大佬的博客————圖的深度優先遍歷和廣度優先遍歷理解
這兩道題改了選項,我當時沒注意到羣裏面的,好氣哦
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | |
---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 |
第一週 | 0/0 | 1/1 | 8/8 |
第二週 | 671/671 | 1/2 | 17/25 |
第三週 | 345/1016 | 1/3 | 15/40 |
第四周 | 405/1421 | 2/5 | 23/63 |
第五週 | 1202/2623 | 1/5 | 20/83 |
第六週 | 1741/4364 | 1/6 | 20/103 |
第七週 | 400/4764 | 1/7 | 20/123 |
第八週 | 521/5285 | 2/9 | 24/147 |
第九周 | 1622/6907 | 2/11 | 17/164 |