1. 程式人生 > >詳解資料結構——圖之鄰接矩陣表示法

詳解資料結構——圖之鄰接矩陣表示法

一、圖的建立

圖是表達“多對多”的關係的一種資料結構。

它由非空的有限頂點集合和有限邊集合組成。

1. 頂點集合常常由陣列表示。

陣列下標表示頂點位置。

陣列內容包含頂點資料,並且要新增判定是否被訪問過的標誌標量,為其餘操作提供引數。

其資料型別定義如下:

struct Vertex {
    T data;
    bool isVisited;
}

2. 邊集合的儲存常用鄰接矩陣或者鄰接表。

邊數值(權值)表示頂點之間有某種關係,0表示兩個頂點之間沒有邊,也即沒有關係。

鄰接矩陣是一個二維陣列,matrix[頂點數][頂點數]。也可定義為一個省一半空間的一維陣列 n(n+1)/2,不利於理解,不作說明。

最簡單的圖用來呈現頂點之間的關係。頂點型別可以只包含一個用來判斷是否被訪問過的標誌變數。用bool或者int陣列表示頂點。

<pre name="code" class="cpp">//graph.h
#ifndef GRAGH_H
#define GRAGH_H

class Graph {
public:
Graph(const int &capacity);
~Graph();
void insertEdge(const int &indexA, const int &indexB, const int &weight = 1);

//成員變數只需要三個。
private:
int capacity; //圖的頂點個數。
bool *pVertex; //儲存圖的頂點。
int *pMatrix; //儲存圖的邊。
}

#endif

實現檔案:
//graph.cpp
#include "graph.h"

/*
通過初始化列表建立一張圖。
並對pVertex、pMatrix進行值初始化。
pVertex的每個元素值初始化為false,pMatrix的每個元素值初始化為0.
*/
inline
Graph::Graph(const int &capacity):
    capacity(capacity), 
    pVertex(new bool[capacity]()), 
    pMatrix(new int[capacity * capacity]()) {}

<span style="color:#808000;">inline</span><pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style="color:#800080;">Graph</span><span style="color:#000000;">::~</span><span style="color:#800080;">Graph</span><span style="color:#000000;">()</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">{</span>
delete[]pVertex;
delete[]pMatrix;
}

inline
voidGraph::insertEdge(constint &indexA,const int&indexB,const intweight){
//插入一條從A點指向B點的邊。
pMatrix[indexA*capacity+indexB]=weight;
//無向圖需要插入一條從B指向A的邊。
pMatrix[indexB*capacity+indexA]=weight;
}

1. pVertex下標表示頂點位置。

2. pVertex值表示頂點是否被訪問過,初始化為false。

3. pMatrix值表示頂點之間的關係,初始化為0。

4. 新增邊的資訊,就形成了一張圖。

二、圖的遍歷

有了圖之後,我們就要對它進行一些操作。

首先是遍歷:

1. 深度優先搜尋Depth First Search,類似於樹的先序遍歷。

2. 廣度優先搜尋Breadth First Search,類似於樹的層序遍歷。

為此,在graph.h標頭檔案中新增四個函式:

//graph.h
public:
<span style="color:#808000;">    void</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">DFS</span><span style="color:#000000;">(</span><span style="color:#808000;">const</span><span style="color:#c0c0c0;"> </span><span style="color:#808000;">int</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">&</span>index<span style="color:#000000;">);</span><pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style="color:#c0c0c0;">    </span><span style="color:#808000;">void</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">BFS</span><span style="color:#000000;">(</span><span style="color:#808000;">const</span><span style="color:#c0c0c0;"> </span><span style="color:#808000;">int</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">&</span>index<span style="color:#000000;">);</span>
voidresetVertex();

private:
constint&getEdgeWeight(constint&indexA,constint&indexB);

其中的resetVertex是用來在下一個遍歷之前,重置所有的頂點為未訪問過的狀態。

若是用int陣列表示,則可傳入一個值,用來判定頂點的訪問狀態,也就不需要resetVertex。

而getEdgeWeight函式是返回從A點到B點的邊的權值,代表了兩個頂點的關係。

其實現如下:

//graph.cpp
#include <iostream>
#include <queue>    //包含標頭檔案,為BFS提供佇列容器。

<span style="color:#808000;">inline</span><pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style="color:#808000;">const</span><span style="color:#c0c0c0;"> </span><span style="color:#808000;">int</span><span style="color:#000000;">&</span><span style="color:#c0c0c0;"> </span><span style="color:#800080;">Graph</span><span style="color:#000000;">::</span><span style="color:#000000;">getEdgeWeight</span><span style="color:#000000;">(</span><span style="color:#808000;">const</span><span style="color:#c0c0c0;"> </span><span style="color:#808000;">int</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">&</span><span style="color:#000000;">indexA</span><span style="color:#000000;">,</span><span style="color:#c0c0c0;"> </span><span style="color:#808000;">const</span><span style="color:#c0c0c0;"> </span><span style="color:#808000;">int</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">&</span><span style="color:#000000;">indexB</span><span style="color:#000000;">)</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">{</span>
returnpMatrix[indexA*capacity+indexB];
}

//打印出從index開始,進行深度優先的索引次序。
voidGraph::DFS(constint&index){
std::cout<<index<<"";
pVertex[index]=true;

for(inti(0);i<capacity;++i){
if(getEdgeWeight(index,i)>0&&!pVertex[i])
DFS(i);
}
}

/打印出從index開始,進行廣度優先的索引次序。
voidGraph::BFS(constint&index){
std::queue<int>que;
que.push(index);
pVertex[index]=true;

while(!que.empty()){
intsubcript(que.front());
que.pop();
std::cout<<subcript<<"";

for(inti(0);i<capacity;++i){
if(getEdgeWeight(subcript,i)>0&&!pVertex[i]){
que.push(i);
pVertex[i]=true;
}
}
}
}

//重置頂點為未訪問過。
voidGraph::resetVertex(){
for(inti(0);i<capacity;++i)
pVertex[i]=false;
}


三、最短路徑Shortest Path

圖的重要應用之一,最短路徑。

。。。怎麼感覺寫部落格好累啊,寫了一個多小時,才寫了這麼點兒。

後面的最短路徑和最小生成樹感覺這樣寫下去得四五個小時。

有空再寫完。