c++ 图的连通分量是什么_学习数据结构第五章:图(图的遍历操作)
第五章:圖(圖的遍歷操作)
1.圖的遍歷
圖的遍歷:從圖中某一頂點出發,按照某種搜索方法沿著圖中的邊對圖中的所有頂點訪問依次且僅訪問一次
其實樹的層次遍歷和圖的廣度優先搜索類似,可以把這個二叉樹看成一個圖
2.廣度優先搜索(BFS)
廣度優先搜索
- 首先訪問起始頂點v
- 接著由v出發依次訪問v的各個 未被訪問過 的鄰接頂點w1,w1....wi
- 然后依次訪問w1,w2....wi的所有 未被訪問過 的鄰接頂點
- 在從這些訪問過的頂點出發,訪問它們所有 未被訪問過 的鄰接頂點
- 以此類推
如上圖,它的 廣度優先搜索遍歷 ,我們如果按照 樹的層次遍歷 這種方式進行遍歷它的過程如下:
首先 1,然后訪問它的所有鄰接頂點即 2,3,然后分別訪問 2 和 3 的鄰接頂點,2 的 4 和5 ,3 的 6 ,接著我們從頂點 4 出發依舊訪問它的所有鄰接頂點即 7 和 5,此時我們就會發生錯誤,因為頂點 5 我們已經訪問過了,再訪問就不符合 廣度優先搜索 的要求了, 所以我們遍歷的時候不能完全按照樹的層次遍歷的方式進行遍歷圖 ,那么怎么實現圖的廣度優先搜索呢?
我們知道樹的層次遍歷我們借助了一種特殊的數據結構:隊列
那么圖我們不僅僅要依靠隊列還要依靠一個輔助標記數組即:隊列+輔助標記數組
輔助標記數組我們依舊使用上面的圖,首先我們需要初始化,即將輔助標記數組中的值全部置為0,即0表示未被訪問,1表示已被訪問。重新進行遍歷:從 1 出發,首先 1 入隊,接著修改 v[0]=1 ,接著出隊隊首元素 1 并訪問,接著我們需要將 1 的鄰接頂點 2 和 3 一次入隊,接著修改 v[1]=1,v[2]=1 ,然后出隊隊首元素 2 并進行訪問,接著我們需要將頂點 2 的鄰接頂點分別 1,4,5進行入隊,這里我們其實只入隊了 4 和 5,接著修改 v[3]=1,v[4]=1 ,這里有一個判斷過程就是利用這個輔助標記數組,接著出隊隊首隊首元素 3 并訪問,然后將頂點 3 鄰接的頂點并且未入隊的頂點進行入隊操作,即頂點 6 入隊,同時修改a[5]=1,接著出隊隊首元素 4 并訪問,然后入隊 頂點 4 的鄰接的頂點并且未入隊的頂點 7 ,同時修改 v[6]=1,接著出隊隊首元素 5 并訪問,接著出隊隊首元素 7 并訪問,接著出隊隊首元素 7 并訪問,遍歷完成。此時數組中所有頂點的值都是1.
代碼實現
bool visited[MAX_TREE_SIZE] void BFSTraverse(Graph G){ for(int i=0; i visited[i]=FALSE; } InitQueue(Q); //for循環的作用,我們上面講的是一個連通圖,所有頂點都可以通過一個頂點依次進行訪問 //但是如果一個圖不是連通的,我們需要遍歷所有頂點 for(int i=0;i if(!visited(i)){ BFS(G,i); } }}//廣度優先搜索void BFS(Graph G,int V){ visit(v); //訪問 visited=TRUE; //TRUE 入隊了,FALSE未入隊 EnQueue(Q,v); //將結點入隊 while(!isEmpty(Q)){ //判斷隊列是否是空 DeQueue(Q,v); //出隊隊首元素,并賦值到v中 //FirstNeighbor 求圖G中頂點x的第一個鄰接頂點,存在返回頂點號,不存在返-1 //判斷 w是否大于0,如果是-1則說明沒有鄰接頂點了 //NextNeighbor 求圖G中頂點v的的下一個鄰接頂點并賦值給w for(int w=FirstNeighbor(G,v);w>0;w=NextNeighbor(G,v,w)){ if(!visited[w]){//判斷是否入隊過 visit[w]; visited[w]=TRUE; EnQueue(Q,w); } } }}3.應用
3.1無權圖單源最短路徑問題
定義從頂點u到頂點v的最短路徑d(u,v)為從u到v的任何路徑中最少的邊數,若從u到v沒有通路,則d(u,v)=∞(表示不可達到)
//和廣度優先搜索相似,增加了保存最短路徑的一個數組void BFS_MIN_Distance(Graph G,int u){//傳入圖 和 初始頂點 for(int i=0;i d[i]=MAX; //保存最短路徑的值,我們初始化為最大值 } visited[u]=TRUE;//標識為該頂點已經入隊 d[u]=0;//初始頂點路徑值改為0 EnQueue(Q,u);//入隊 while(!isEmpty(Q)){//判斷隊列時候為空 DeQueue(Q,u);//出隊隊首元素 //FirstNeighbor 求圖G中頂點x的第一個鄰接頂點,存在返回頂點號,不存在返-1 //判斷 w是否大于0,如果是-1則說明沒有鄰接頂點了 //NextNeighbor 求圖G中頂點v的的下一個鄰接頂點并賦值給w for(int w=FirstNeighbor(G,u);w>0;w=NextNeighbor(G,u,w)){ if(!visit[w]){//判斷該頂點是否已經被訪問過 visited[w]=TRUE;//設置被訪問過 //d[u]表示到初始頂點的最短路徑,w是它的鄰接點,所以+1等目前w到初始頂點的最短路徑 d[w]=d[u]+1; EnQueue(Q,w);//入隊 } } }}3.2廣度優先生成樹
廣度優先生成樹:在廣度遍歷過程中,我們可以得到一顆遍歷樹,稱為廣度優先生成樹(生成森林)
如果是一個連通圖我們會得到是一顆生成樹,而如果是非連通圖我們得到的是生成森林
連通圖:任意兩個結點都是連通的
鄰接矩陣法的廣度優先生成樹是唯一的,鄰接表法的不唯一
因為一個圖的鄰接矩陣表示是唯一的,所以我們在進行遍歷的過程也是唯一的,但是鄰接表表示法中我們輸入的次序不唯一生成的邊表就不唯一,對應遍歷的過程(遍歷邊的次序)就不唯一了
4.深度優先搜索(DFS)
廣度優先搜索和樹層次遍歷比較類似,而深度優先搜索和樹先序遍歷比較類似,如果把這樣的一個樹看成一個圖,它的先序遍歷順序就是圖的深度優先搜索遍歷順序
我們可以發現廣度優先搜索是按照圖的寬度范圍進行遍歷,而深度優先搜索是按照一條路徑的深度的走向進行搜索遍歷的
深度優先搜索(DFS)
- 首先訪問起始頂點v
- 接著由v出發訪問v的任意一個鄰接且未被訪問的鄰接頂點Wi
- 然后再訪問與Wi鄰接且未被訪問的任意頂點Yi
- 若Wi沒有鄰接且未被訪問的頂點時,退回到它的上一層頂點v
- 重復上述過程,直到所有頂點被訪問為止
我們通過上面的算法思想遍歷一遍上圖:首先訪問 1 ,接著可以任意訪問頂點 2 或 3,我們訪問 2,接著我們可以 ?訪問任意頂點 4 或 5,我們訪問 4 ,然后我們可以訪問任意的頂點 7 或者 5 (7和5也是4的鄰接頂點),假設我們訪問5,然后 5 沒有鄰接且未被訪問的頂點我們退回到 4 ,接著訪問頂點 7,接著退回到頂點 1 ,然后訪問頂點 3 ,接著訪問 6,遍歷完成:1 2 4 5 7 3 6
我們從上面的遍歷過程可以發現整個過程可以使用遞歸來實現,當然遞歸也可以轉換成棧來實現,同時我們也需要一個輔助標記數組。即:遞歸(棧)+輔助標記數組
代碼實現
bool visited[MAX_TREE_SIZE]//輔助標記數組void DFSTraverse(Graph G){ for(int i=0;i visited[i]=FALSE;//初始化所有結點都未必訪問 } for(int i=0;i if(!visited[i]){//如果結點未被訪問 DFS(G,i);//G:圖,i:起始頂點的編號 } }}void DFS(Graph G,int v){ visit(v); //訪問 visited[v]=TRUE;//置為訪問過 for(int w=FirstNeighbor(G,v);w>0;w=NextNeighbor(G,v,w)){ if(!visit[w]){//判斷該頂點是否已經被訪問過 DFS(G,w);//遞歸 } }}如上圖,我們通過上面的代碼來遍歷一遍:從A出發,訪問A并把A對應的輔助標記數組值設置為TREUE,接著我們找A的第一個鄰接頂點比如是C,然后我們判斷C沒有被訪問過,接著繼續調用DFS函數,訪問C,同理繼續找C的第一個鄰接頂點D,判斷D沒有被訪問過,我們訪問頂點D,然后D沒有鄰接頂點此函數結束,我們退回到訪問C的頂點的DFS的for循環中,找到C的第二個鄰接點E,判斷E沒有被方問過,我們訪問E,接著E沒有鄰接頂點,繼續退回到C,C沒有未被訪問過的鄰接頂點,我們退回到A的DFS的for循環中,發現A的第二個鄰接頂點E被訪問過了,所以A也沒有未被訪問過的鄰接頂點了,我們退回到了DFSTraverse函數的第二個for循環中,循環判斷到B發現B為被訪問,然后調用DFS函數,訪問D,設為TRUE,然后發現B不存在未被訪問的鄰接頂點,所以退回到DFSTraverse函數的第二個for循環中,發現沒有未被訪問過的頂點了,所以遍歷結束:ACDEB
從此過程可以看出第一個函數的作用就是如果我們的初始頂點無法完成遍歷圖中的所有頂點我們就可以通過循環遍歷每一個頂點。
鄰接矩陣法的DFS(BFS)序列唯一,鄰接表法的不唯一
5.深度優先生成樹
深度優先生成樹:在深度遍歷過程中我們可以得到一顆遍歷樹,稱為深度優先生成樹(生成森林)
鄰接矩陣法的深度度優先生成樹是唯一的,鄰接表法的不唯一
6.遍歷與連通性問題
如何通過遍歷來判斷該圖的連通性?
上面是一個無向圖:無論我們使用BFS還是DFS都能通過任何一個頂點訪問到其他的頂點,所以他是一個連通圖
所以我們有以下結論:在無向圖中,在任意結點出發進行一次遍歷(調用一次BFS或者DFS),若能訪問全部結點,說明該圖是連通的。
上面是一個非連通的無向圖,我們在進行遍歷(BFS或DFS)的時候為了遍歷到每一個頂點,我們需要一個for循環對每一個頂點進行調用BFS或者DFS。
我們由此可以得到如下結論:在無向圖中,調用遍歷函數(BFS或DFS)的次數為連通分量的個數
如上面是一個有向圖:我們從頂點B出發DFS可以遍歷到任何頂點,但是能訪問到所有頂點代表這個圖是一個強連通圖嗎?答案當然是不是的,能遍歷到所有頂點只能說明從某個頂點到另一個頂點有一條有向突擊。
如果上圖我們從頂點A開始遍歷,我們則需要調用兩次DFS,所以在有向圖中,調用遍歷函數(BFS或DFS)的次數為不是強連通分量的個數
無向圖叫連圖,有向圖叫強連通
總結
以上是生活随笔為你收集整理的c++ 图的连通分量是什么_学习数据结构第五章:图(图的遍历操作)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快手怎么换手机号码绑定
- 下一篇: c++ stack 遍历_C/C++内存