http://wenku.baidu.com/link?url=fCPps803r8as33U38hMJ8ZtekPooj76vScbCtvS_PMO7FNdgmXhAeQlbeJT_jzQUMGFk4AWuGrUUfsumJPJPuycU-oikupB4gaCoWlVCynO
*問題描述:
建立圖的儲存結構(圖的類型可以是有向圖、無向圖、有向網、無向網,學生可以任選兩種類型),能夠輸入圖的頂點和邊的資訊,並儲存到相應儲存結構中,而後輸出圖的鄰接矩陣。
1、鄰接矩陣標記法:
設G=(V,E)是一個圖,其中V={V1,V2,V3…,Vn}。G的鄰接矩陣是一個他有下述性質的n階方陣:
1,若(Vi,Vj)∈E 或<Vi,Vj>∈E;
A[i,j]={
0,反之
圖5-2中有向圖G1和無向圖G2的鄰接矩陣分別為 M1和 M2:
M1=┌ 0 1 0 1 ┐
│ 1 0 1 0 │
│ 1 0 0 1 │
└ 0 0 0 0 ┘
M2=┌ 0 1 1 1 ┐
│ 1 0 1 0 │
│ 1 1 0 1 │
└ 1 0 1 0 ┘
注意無向圖的鄰接是一個對稱矩陣,例如 M2。
用鄰接矩陣標記法來表示一個具有n個頂點的圖時,除了用鄰接矩陣中的n*n個元素儲存頂點間相鄰關係外,往往還需要另設一個向量儲存n個頂點的資訊。因此其類型定義如下:
VertexType vertex[MAX_VERTEX_NUM]; // 頂點向量
AdjMatrix arcs; // 鄰接矩陣
int vexnum, arcnum; // 圖的當前頂點數和弧(邊)數
GraphKind kind; // 圖的種類標誌
若圖中每個頂點只含一個編號i(1≤i≤vnum),則只需一個二維數組表示圖的鄰接矩陣。此時儲存結構可簡單說明如下:
type adjmatrix=array[1..vnum,1..vnum]of adj;
利用鄰接矩陣很容易判定任意兩個頂點之間是否有邊(或弧)相聯,並容易求得各個頂點的度。
對於無向圖,頂點Vi的度是鄰接矩陣中第i行元素之和,即
n n
D(Vi)=∑A[i,j] (或∑A[i,j])
j=1 i=1
對於有向圖,頂點Vi的出度OD(Vi)為鄰接矩陣第i行元素之和,頂點Vi的入度ID(Vi)為第i列元素之和。即
n n
OD(Vi)=∑A[i,j], OD(Vi)=∑A[j,i])
j=1 j=1
用鄰接矩陣也可以表示帶權圖,只要令
Wij, 若<Vi,Vj>或(Vi,Vj)
A[i,j]={
∞ , 否則。
其中Wij為<Vi,Vj>或(Vi,Vj)上的權值。相應地,網的鄰接矩陣表示的類型定義應作如下的修改: adj:weightype ; {weightype為權類型}
圖5-6列出一個網和它的鄰接矩陣。
┌∞31∞∞┐
│∞∞51∞│
│∞∞∞∞∞│
│∞∞6∞∞│
└∞322∞┘
(a)網 (b)鄰接矩陣
圖5-6 網及其鄰接矩陣
對無向圖或無向網路,由於其鄰接矩陣是對稱的,故可採用壓縮存貯的方法,僅存貯下三角或上三角中的元素(但不含對角線上的元素)即可。顯然,鄰接矩陣標記法的空間複雜度O( )。
無向網鄰接矩陣的建立方法是:首先將矩陣A的每個元素都初始化成∞。然後,讀入邊及權值(i,j,wij),將A的相應元素置成Wij。
2、圖的遍曆:
*深度優先搜尋
深度優先搜尋遍曆類似於樹的先根遍曆,是樹的先根遍曆的推廣。假設初始狀態是圖中所有的頂點未曾被訪問,則深度優先遍曆可從圖的某個頂點V出發,訪問此頂點,然後依次從V的未被訪問的鄰接點出發深度優先遍曆圖,直至圖中所有和V有路徑相通的頂點都被訪問到;若此時圖中尚有頂點未被訪問,則另選圖中的一個未被訪問的頂點,重複上述過程,直至圖中所有頂點都被訪問到為止。
以圖7.13(a)中無向圖G4為例,深度優先遍曆圖的過程如圖7.13(b)所示。假設從頂點V1出發進行搜尋,在訪問了頂點V1後,選擇鄰接點V2。因為V2未曾訪問,則從V2出發進行搜尋。依次類推,接著從V4,V8,V5出發進行搜尋。在訪問了V5之後,由於V5的鄰接點已都被訪問,則搜尋回到V8。由於同樣的理由,搜尋繼續回到V4,V2直至V1,此時由於V1的另一個鄰接點為被訪問,則搜尋又從V1到V3,再繼續進行下去。由此得到頂點的訪問序列為:
V1V2 V4V8V5V3V6V7
顯然,這是一個遞迴的過程。為了在遍曆過程中便於區別頂點是否已被訪問,需附設訪問標誌數組visted[0...n-1],其初值為0,一但某個頂點被訪問,則其相應的分量置為1。
*廣度優先搜尋
假設從圖中某頂點v出發,在訪問了v之後一次訪問v的各個未曾訪問的擴大鄰接點,然後分別從這些鄰接點出發依次訪問他們的鄰接點,並使“先被訪問的鄰接點”先於“後被訪問的鄰接點”被訪問,直至圖中所有已被訪問的頂點的鄰接點都被訪問到。若圖中尚有頂點未被訪問,則另選圖中一個未曾被訪問的頂點作起始點,重複上述過程,直到圖中的頂點都被訪問為止。換句話說,廣度優先遍曆圖的過程就是以v為起始點,有遠至近,依次訪問和v有路徑相通且路徑長度為1、2……的頂點。例如,對圖G4進行廣度優先搜尋遍曆的過程如圖7.13(3)所示,首先訪問v1和v1的鄰接點v2和v3,然後依次訪問v2的鄰接點v4和v5及v3的鄰接點v6和v7,最後訪問v4的鄰接點v8。由於這些頂點的鄰接點均已被訪問,並且圖中所有頂點都被訪問,由此完成了圖的遍曆。得到的頂點訪問序列為
V1V2 V3 V4 V5V6 V7 V8
和深度優先搜尋類似,在遍曆的過程中也需要一個訪問標誌數組。並且,為了順次訪問路徑長度為2、3、…的頂點,需附設隊列以儲存已被訪問的路徑長度為1、2…的頂點。
2、圖的輸出
圖的鄰接矩陣是一個二維數組,運用for語句的嵌套依次輸出。
Y
N
Y
N Y
N 主程式流程圖
Y
N
圖的構造流程圖
1、無向圖鄰接矩陣的建立演算法如下:
procedure build-graph; {建立無向圖的鄰接矩陣}
begin
for i:=1 to n do read(G.vertex[i]); {讀入n個頂點的資訊}
for i:=1 to n do
for j:=1 to e do
G.arcs[i][j]=0;
{將鄰接矩陣的每個元素初始化成0 }
for k:=1 to e do {e為邊的數目}
[ read(i,j,w) {讀入邊<i,j>和權}G.arcs[i][j]:=w]
G.arcs[i][j]=G.arcs[i][i]{置對稱弧}
end;
該演算法的執行時間是O(n+n2+e),其中消耗在鄰接矩陣初始化操作上的時間是O(n2),而e<n2,所以上述演算法的時間複雜度是O(n2)。
2、無向網鄰接矩陣的建立演算法如下:
procedure build-graph; {建立無向網的鄰接矩陣}
begin
for i:=1 to n do read(G.vertex[i]); {讀入n個頂點的資訊}
for i:=1 to n do
for j:=1 to e do
G.arcs[i][j]=maxint;
{將鄰接矩陣的每個元素初始化成maxint,電腦內∞用最大事數maxint表示}
for k:=1 to e do {e為邊的數目}
[ read(i,j,w) {讀入邊<i,j>和權}G.arcs[i][j]:=w;G.arcs[i][j]:=w]end;
該演算法的執行時間是O(n+n2+e),其中消耗在鄰接矩陣初始化操作上的時間是O(n2),而e<n2,所以上述演算法的時間複雜度是O(n2)。
3、圖的深度優先遍曆演算法分析
begin
for i:=1 to n do(visited[i]){初始化標誌數組}
while (i<n)
{for:i=1 to n do{按要求訪問鄰接點}}
end
當用二維數組表示鄰接矩陣作圖的儲存結構時,尋找每個頂點的鄰接點所需時間為O(n2),其中n為圖中頂點數。
4、圖的廣度優先遍曆演算法分析
begin
for i:=1 to n do(visited[i]){初始化標誌數組}
while (i<n)
{for:i=1 to n do{if…..if…..}}
end
二維數組表示鄰接矩陣作圖的儲存結構,其中n為圖中頂點數,尋找每個頂點的鄰接點所需時間為O(n2)。
/* Graph.h */
#include<stdio.h>
#include<malloc.h>
#include<conio.h>
#include<stdlib.h>
#include<string.h>
#define ERROR 0
#define OK 1
#define MAX_VERTEX_NUM 20 //定義最大值
#define INFINITY 32768 //定義極大值
#define MAX_INFO 20
typedefint VrType; //定義新的類型
typedefint InfoType;
typedefchar VertexType;
typedefenum
{DG,DN,UDG,UDN}GraphKind;//有向圖,有向網,無向圖,無向網
typedefstruct ArcCell
{//鄰接矩陣標記法的各個資料結構
VrType adj; // 頂點關聯類型。對無權圖,用或表示相鄰否;對帶權圖,則為權實值型別。
InfoType *info; // 該弧相關資訊的指標
} ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedefstruct
{
VertexType vertex[MAX_VERTEX_NUM]; // 頂點向量
AdjMatrix arcs; // 鄰接矩陣
int vexnum, arcnum; // 圖的當前頂點數和弧(邊)數
GraphKind kind; // 圖的種類標誌
} MGraph;
typedefstruct
{//設定棧
int elem1[MAX_VERTEX_NUM];
int top;
}SeqStack;
int LocateVertex(MGraph G,VertexType v);
void CreateUDG(MGraph &G);
void CreateUDN(MGraph &G);
void DepthFirstSearch1(MGraph G);
void BreadthFirstSearch1(MGraph G);
int CreateGraph(MGraph &G);
void Display(MGraph G);
/* Graph.cpp */
#include"Graph.h"
int LocateVertex(MGraph G,VertexType v)
{//用於返回輸弧端點所表示的數值
int j=0,k;
for(k=0;k<G.vexnum;++k)
if(G.vertex[k]==v)
{j=k;break;}
return(j);
}
void CreateUDG(MGraph &G)
{ // 採用數組(鄰接矩陣)標記法,構造無向圖
int i,j,k,IncInfo;
//i,j,k為計數器,IncInfo為標誌符
char ch; //用於吃掉多餘的字元
VertexType v1,v2; //用於放置輸入的弧的兩個頂點
printf("請輸入無向圖G的頂點數,邊數,弧是否含相關資訊(是:,否:): \n");
scanf("%d,%d,%d",&G.vexnum,&G.arcnum,&IncInfo);
ch=getchar(); //用於吃掉斷行符號
printf("請輸入%d個頂點的值(1個字元,空格隔開):\n",G.vexnum);
for(i=0;i<G.vexnum;++i) // 構造頂點向量
{
scanf("%c",&G.vertex[i]);ch=getchar();
}
printf("請輸入%d條邊的頂點頂點(以空格作為間隔): \n",G.arcnum);
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; // {adj,info}
}
for(k=0;k<G.arcnum;++k)
{
scanf("%c %c",&v1,&v2);
ch=getchar();// ch吃掉斷行符號符
i=LocateVertex(G,v1); j=LocateVertex(G,v2);
if(IncInfo)scanf("%d",&G.arcs[i][j].info);
G.arcs[i][j].adj=G.arcs[j][i].adj=1; // 置<v1,v2>的對稱弧<v2,v1>
}
}//CreateUDG
void CreateUDN(MGraph &G)
{ // 採用數組(鄰接矩陣)標記法,構造無向網
int i,j,k,w,IncInfo;
//i,j,k為計數器,w用於放置權值,IncInfo為標誌符
char ch; //用於吃掉多餘的字元
VertexType v1,v2;