1、鄰接矩陣
用一個一維數組存儲圖中頂點的信息
用一個二維數組(稱爲鄰接矩陣)存儲圖中各頂點之間的鄰接關係。
- 無向圖的鄰接矩陣:主對角線爲 0 且一定是對稱矩陣。
頂點i的度:鄰接矩陣的第i行(或第i列)非零元素的個數。
判斷頂點 i 和 j 之間是否存在邊:測試鄰接矩陣中相應位置的元素arc[i][j]是否爲1。
求頂點 i 的所有鄰接點:將數組中第 i 行元素掃描一遍,若arc[i][j]爲1,則頂點 j 爲頂點 i 的鄰接點。 - 有向圖的鄰接矩陣不一定對稱。
求頂點 i 的出度:鄰接矩陣的第 i 行元素之和。
求頂點 i 的入度:鄰接矩陣的第 i 列元素之和。
判斷從頂點 i 到頂點 j 是否存在邊:測試鄰接矩陣中相應位置的元素arc[i][j]是否爲1。
2、鄰接矩陣存儲無向圖的類
const int MaxSize=10;
template <class T>
class Mgraph{
public:
MGraph(T a[ ], int n, int e );
~MGraph( )
void DFSTraverse(int v);
void BFSTraverse(int v);
……
private:
T vertex[MaxSize];
int arc[MaxSize][MaxSize];
int vertexNum, arcNum;
};
- 構造函數
template <class T>
MGraph::MGraph(T a[ ], int n, int e) {
vertexNum=n; arcNum=e;
for (i=0; i<vertexNum; i++)
vertex[i]=a[i];
for (i=0; i<vertexNum; i++) //初始化鄰接矩陣
for (j=0; j<vertexNum; j++)
arc[i][j]=0;
for (k=0; k<arcNum; k++) {
cin>>i>>j; //邊依附的兩個頂點的序號
arc[i][j]=1; arc[j][i]=1; //置有邊標誌
}
}
- 深度優先遍歷
int visited[MaxSize];
template <class T>
void MGraph::DFSTraverse(int v){
cout<<vertex[v]; visited [v]=1;
for (j=0; j<vertexNum; j++)
if (arc[v][j]==1 && visited[j]==0)
DFSTraverse( j );
}
- 廣度優先搜索
int visited[MaxSize];
template <class T>
void MGraph::BFSTraverse(int v){
front=rear=-1; //假設採用順序隊列且不會發生溢出
int Q[MaxSize]; cout<<vertex[v]; visited[v]=1; Q[++rear]=v;
while (front!=rear) {
v=Q[++front];
for (j=0; j<vertexNum; j++)
if (arc[v][j]==1 && visited[j]==0 ) {
cout<<vertex[j]; visited[j]=1; Q[++rear]=j;
}
}
}
- 增加一個頂點
在存儲頂點的一維數組中插入該頂點的信息
在鄰接矩陣中插入一行、一列
template <class T>
void MGraph<T>::InsertVex(int num,T name) {
if ( num<0|| num>vertexNum) throw "位置";
int row, col, numv;
numv = vertexNum-1;
vertexNum++;
for(int i=numv;i>=num;i--) vertex[i++]=vertex[i];
vertex[num]=name;
for(row=numv;row>=0;row--) {所有行上num列之後的列後移,增加一列,
for(col=numv;col>=num;col--) arc[row][col+1]=arc[row][col];
arc[row][num]=0;
}
for(row=numv;row>=num;row--)
for(col=0;col<=numv+1;col++) arc[row+1][col]=arc[row][col];
for(col=0;col<vertexNum;col++) arc[num][col]=0;
}
- 刪除一個頂點
在存儲頂點的一維數組中刪除該頂點的信息
在鄰接矩陣中刪除一行、一列
template <class T> void MGraph<T>::DeleteVex(int pos){
if ( pos<0|| pos>MaxSize) throw "位置";
int row, col;
int numv=vertexNum;
for(int i=pos;i<numv;i++) vertex[i]=vertex[i+1];
vertexNum--;
for(row=0;row<numv;row++) { //刪除一列
for(col=pos;col<numv;col++) arc[row][col]=arc[row][col+1];
}
for(row=pos;row<numv;row++)
for(col=0;col<numv;col++)
arc[row][col]=arc[row+1][col];
}
}
- 增加一條邊
修改相應的矩陣元素的值
tmplate <class T>
void MGraph<T>::InsertArc(int i, int j)
{
if ( i>MaxSize|| j>MaxSize) throw "位置";
arc[i][j]=1;
arc[j][i]=1;
}
- 刪除一條邊
修改相應的矩陣元素的值
template <class T>
void MGraph<T>::DeleteArc(int i, int j)
{
if ( i>MaxSize|| j>MaxSize) throw "位置";
arc[i][j]=arc[j][i]=0;
}
3、鄰接表存儲的基本思想:
對於圖的每個頂點vi,將所有鄰接於vi的頂點鏈成一個單鏈表,稱爲頂點vi的邊表(對於有向圖則稱爲出邊表)
所有邊表的頭指針和存儲頂點信息的一維數組構成了頂點表。
- 鄰接表有兩種結點結構:頂點表結點和邊表結點。
vertex:數據域,存放頂點信息。
firstedge:指針域,指向邊表中第一個結點。
adjvex:鄰接點域,邊的終點在頂點表中的下標。
next:指針域,指向邊表中的下一個結點。
定義鄰接表的結點:
struct ArcNode{
int adjvex;
ArcNode *next;
};
template <class T>
struct VertexNode{
T vertex;
ArcNode *firstedge;
};
- 無向圖的鄰接表
求頂點 i 的度:頂點i的邊表中結點的個數。
判斷頂點 i 和頂點 j之間是否存在邊:測試頂點 i 的邊表中是否存在終點爲 j 的結點。 - 有向圖的鄰接表(出邊表)
求頂點 i 的出度:頂點 i 的出邊表中結點的個數。
求頂點 i 的入度:各頂點的出邊表中以頂點 i 爲終點的結點個數。
求頂點 i 的所有鄰接點:遍歷頂點 i 的邊表,該邊表中的所有終點都是頂點 i 的鄰接點。 - 有向圖的逆鄰接表(入邊表)
4、鄰接表存儲有向圖的類
const int MaxSize=10; //圖的最大頂點數
template <class T>
class ALGraph
{
public:
ALGraph(T a[ ], int n, int e);
~ALGraph;
void DFSTraverse(int v);
void BFSTraverse(int v);
………
private:
VertexNode adjlist[MaxSize];
int vertexNum, arcNum;
};
- 構造函數
template <class T>
ALGraph::ALGraph(T a[ ], int n, int e)
{
vertexNum=n; arcNum=e;
for (i=0; i<vertexNum; i++)
{
adjlist[i].vertex=a[i];
adjlist[i].firstedge=NULL;
}
for (k=0; k<arcNum; k++)
{
cin>>i>>j;
s=new ArcNode; s->adjvex=j;
s->next=adjlist[i].firstedge;
adjlist[i].firstedge=s;
}
}
- 深度優先遍歷
template <class T>
void ALGraph::DFSTraverse(int v){
cout<<adjlist[v].vertex; visited[v]=1;
p=adjlist[v].firstedge;
while (p!=NULL) {
j=p->adjvex;
if (visited[j]==0) DFSTraverse(j);
p=p->next;
}
}
- 廣度優先遍歷
template <class T>
void ALGraph::BFSTraverse(int v){
front=rear=-1;
cout<<adjlist[v].vertex; visited[v]=1; Q[++rear]=v;
while (front!=rear) {
v=Q[++front]; p=adjlist[v].firstedge;
while (p!=NULL) {
j= p->adjvex;
if (visited[j]==0) {
cout<<adjlist[j].vertex; visited[j]=1; Q[++rear]=j;
}
p=p->next;
}
}
}
- 增刪頂點
增加:頂點表中插入一個元素
刪除:在頂點表中刪除一個元素,同時在邊表中刪除相應的邊 - 增刪邊<x, y>
如果是有向圖,則在x的邊表中增加/刪除邊;
如果是無向圖,則還要在y的邊表中增加/刪除一條邊
5、圖的存儲結構
鄰接矩陣
有向圖和無向圖
鄰接表
有向圖(出邊表)和無向圖
逆鄰接表(有向圖的入邊表)
有向圖,方便計算頂點的入度
有向圖的十字鏈表
無向圖的鄰接多重表
邊集數組
6、十字鏈表:有向圖的鏈式存儲結構 。將鄰接表與逆鄰接表合二爲一。
本質是將在有向圖的鄰接表和逆鄰接表中兩次出現的同一條弧用一個結點表示 。
十字鏈表的結點結構:
vertex:數據域,存放頂點信息;
firstin:入邊表頭指針;
firstout:出邊表頭指針;
tailvex:弧的起點在頂點表中的下標;
headvex:弧的終點在頂點表中的下標;
headlink:入邊表指針域;
taillink:出邊表指針域。
7、鄰接多重表 :無向圖的存儲結構。
8、邊集數組
利用兩個一維數組
一個數組存儲頂點信息,
··另外一個數組存儲邊及其權
··數組分量包含三個域:邊所依附的兩個頂點,權值
··各邊在數組中的次序可以任意。
Struct edge
{
int i;
int j;
int weight;
}
edge edges[M];//邊的數據結構類型的變量
for ( i = 0; i < G->vexnum; i++) {
for (j = 0; j <= G->vexnum; j++) {
if (G->arc[i][j] == 1) {
edges[k].begin = i;
edges[k].end = j;
// edges[k].weight = G->arc[i][j];
k++;
}
}
9、圖的存儲結構的比較——鄰接矩陣和鄰接表