資料結構-圖-鄰接表
阿新 • • 發佈:2018-12-14
使用鄰接矩陣有它的優點:易於求結點度,求鄰接點,易判斷兩點間是否有弧相連。但不利於稀疏圖的儲存,因弧不存在時也要儲存相應資訊。且要預分配足夠大空間。
下面來介紹使用鄰接表的方式來儲存圖。
在鄰接表中,對圖中每個頂點建立一個單鏈表,第i個單鏈表中的結點表示依附與頂點vi的邊,對有向圖是以頂點vi為尾的弧。每個頂點以3個域組成,其中鄰接點域(adjvex)指示與頂點vi鄰接的點在圖中的位置,鏈域(nextarc)指示下一條邊或弧的結點,資料域(Info)儲存和邊或弧相關的資訊,如權值等。每個連結串列上附設一個表頭結點。在表頭結點中,除了設有鏈域(firstarc)指向連結串列中第一個結點之外,還設有儲存頂點vi的名或其他有關資訊的資料域(data)。
首先是輔助巨集的定義:
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define OVERFLOW -1 #define UNDERFLOW -2 #define MAX_INFO 50 #define MVNUM 100 #define maxn 100 #define maxm 100 #define INFINITY 32767 //一定要很大 typedef int Status; typedef char VertexType; typedef char InfoType; typedef enum{DG,DN,UDG,UDN} GraphKind; //圖型別 有向圖,無向圖,有向網,無向網
鄰接表的儲存結構定義:
typedef struct ArcNode{//弧結點 int adjvex; InfoType *Info; //弧的附加資訊 double adj; //鄰接數 1或w struct ArcNode *nextarc; }ArcNode; typedef struct VNode{ //圖結點 VertexType data; ArcNode *firstarc; //頭結點 }VNode,*AdjList; //圖的鄰接表儲存結構定義 typedef struct{ AdjList vertices; //鄰接表 int vexnum,arcnum; //圖結點數,弧數 GraphKind kind; //圖型別 }ALGraph;
若G中存在頂點v 返回v的位置 否則返回-1.
int LocateVex(ALGraph &G,VertexType v){
//若G中存在頂點v 返回v的位置 否則返回-1
for(int i=0;i<G.vexnum;i++)
if(G.vertices[i].data==v)
return i;
return -1;
}
圖的建立.輸入圖的種類和邊頂點構造圖.
Status CreateGraph(ALGraph &G){
//圖的建立
//輸入圖的種類和邊頂點構造圖
scanf("%d",&G.kind);
switch(G.kind){
case DG: return CreateDG(G);
case DN: return CreateDN(G);
case UDG: return CreateUDG(G);
case UDN: return CreateUDN(G);
default: return ERROR;
}
}
建立有向圖G.
Status CreateDG(ALGraph &G){
//建立有向圖G
int i,j,k;
VertexType v1,v2;
ArcNode *arc;
scanf("%d %d",&G.vexnum,&G.arcnum);
if(!(G.vertices=(AdjList)malloc(G.vexnum*sizeof(VNode)))) //建立鄰接表
exit(OVERFLOW);
for(i=0;i<G.vexnum;i++){
scanf(" %c",&G.vertices[i].data); //輸入結點
G.vertices[i].firstarc=NULL;
}
for(k=0;k<G.arcnum;k++){ //輸入弧
scanf(" %c %c",&v1,&v2);
i=LocateVex(G,v1);
j=LocateVex(G,v2);
if(!(arc=(ArcNode *)malloc(sizeof(ArcNode))))
exit(OVERFLOW);
arc->adjvex=j;
arc->adj=1.0;
arc->Info=NULL;
arc->nextarc=G.vertices[i].firstarc; //插入到開始位置 鄰接點與輸入逆序排列
G.vertices[i].firstarc=arc;
}
return OK;
}
建立無向圖G.
Status CreateDN(ALGraph &G){
//建立無向圖G
int i,j,k;
VertexType v1,v2;
ArcNode *arc;
scanf("%d %d",&G.vexnum,&G.arcnum);
if(!(G.vertices=(AdjList)malloc(G.vexnum*sizeof(VNode))))
exit(OVERFLOW);
for(i=0;i<G.vexnum;i++){
scanf(" %c",&G.vertices[i].data);
G.vertices[i].firstarc=NULL;
}
for(k=0;k<G.arcnum;k++){
scanf(" %c %c",&v1,&v2);
i=LocateVex(G,v1);
j=LocateVex(G,v2);
if(!(arc=(ArcNode *)malloc(sizeof(ArcNode))))
exit(OVERFLOW);
arc->adjvex=j;
arc->adj=1;
arc->Info=NULL;
arc->nextarc=G.vertices[i].firstarc;
G.vertices[i].firstarc=arc;
if(!(arc=(ArcNode *)malloc(sizeof(ArcNode)))) //新增對稱弧
exit(OVERFLOW);
arc->adjvex=i;
arc->adj=1;
arc->Info=NULL;
arc->nextarc=G.vertices[j].firstarc;
G.vertices[j].firstarc=arc;
}
G.arcnum*=2;
return OK;
}
建立有向網G.
Status CreateUDG(ALGraph &G){
//建立有向網G
int IncInfo,i,j,k;
double w;
char s[MAX_INFO];
VertexType v1,v2;
ArcNode *arc;
scanf("%d %d %d",&G.vexnum,&G.arcnum,&IncInfo);
if(!(G.vertices=(AdjList)malloc(G.vexnum*sizeof(VNode))))
exit(OVERFLOW);
for(i=0;i<G.vexnum;i++){
scanf(" %c",&G.vertices[i].data);
G.vertices[i].firstarc=NULL;
}
for(k=0;k<G.arcnum;k++){
scanf(" %c %c %lf",&v1,&v2,&w);
i=LocateVex(G,v1);
j=LocateVex(G,v2);
if(!(arc=(ArcNode *)malloc(sizeof(ArcNode))))
exit(OVERFLOW);
arc->adjvex=j;
arc->adj=w;//最短路徑時要判斷大小再賦值
arc->Info=NULL;
if(IncInfo){ //如果有備註資訊
scanf("%s",s);
int l=strlen(s);
if(!(arc->Info=(InfoType *)malloc((l+1)*sizeof(InfoType))))
exit(OVERFLOW);
strcpy(arc->Info,s);
}
arc->nextarc=G.vertices[i].firstarc;
G.vertices[i].firstarc=arc;
}
return OK;
}
建立無向網G.
Status CreateUDN(ALGraph &G){
//建立無向網G
int IncInfo,i,j,k,l;
double w;
char s[MAX_INFO];
VertexType v1,v2;
ArcNode *arc;
scanf("%d %d %d",&G.vexnum,&G.arcnum,&IncInfo);
if(!(G.vertices=(AdjList)malloc(G.vexnum*sizeof(VNode))))
exit(OVERFLOW);
for(i=0;i<G.vexnum;i++){
scanf(" %c",&G.vertices[i].data);
G.vertices[i].firstarc=NULL;
}
for(k=0;k<G.arcnum;k++){
scanf(" %c %c %lf",&v1,&v2,&w);
i=LocateVex(G,v1);
j=LocateVex(G,v2);
if(!(arc=(ArcNode *)malloc(sizeof(ArcNode))))
exit(OVERFLOW);
arc->adjvex=j;
arc->adj=w;
arc->Info=NULL;
if(IncInfo){
scanf("%s",s);
l=strlen(s);
if(!(arc->Info=(InfoType *)malloc((l+1)*sizeof(InfoType))))
exit(OVERFLOW);
strcpy(arc->Info,s);
}
arc->nextarc=G.vertices[i].firstarc;
G.vertices[i].firstarc=arc;
if(!(arc=(ArcNode *)malloc(sizeof(ArcNode))))
exit(OVERFLOW);
arc->adjvex=i;
arc->adj=w;
arc->Info=NULL;
arc->nextarc=NULL;
if(IncInfo){
if(!(arc->Info=(InfoType *)malloc((l+1)*sizeof(InfoType))))
exit(OVERFLOW);
strcpy(arc->Info,s);
}
arc->nextarc=G.vertices[j].firstarc;
G.vertices[j].firstarc=arc;
}
G.arcnum*=2;
return OK;
}
銷燬一個圖G.
Status DestroyGraph(ALGraph &G){
//銷燬一個圖G
int i;
for(i=0;i<G.vexnum;i++){
ArcNode *p=G.vertices[i].firstarc,*q;
while(p){ //遍歷所有結點 銷燬
q=p->nextarc;
if(p->Info){
free(p->Info); //銷燬附加資訊
p->Info=NULL;
}
free(p);
p=NULL;
p=q;
}
}
free(G.vertices); //銷燬鄰接表
G.vertices=NULL;
G.arcnum=0;
G.vexnum=0;
return OK;
}
向圖中插入一個新的結點v.
Status InsertVex(ALGraph &G,VertexType v){
//向圖中插入一個新的結點v
G.vexnum++;
if(!(G.vertices=(AdjList)realloc(G.vertices,G.vexnum*sizeof(VNode)))) //擴充鄰接表
exit(OVERFLOW);
G.vertices[G.vexnum-1].firstarc=NULL; //初始化結點
G.vertices[G.vexnum-1].data=v;
return OK;
}
刪除圖G中結點v 及其向關聯的弧.
Status DeleteVex(ALGraph &G,VertexType v){
//刪除圖G中結點v 及其向關聯的弧
int i=LocateVex(G,v),j;
ArcNode *p=G.vertices[i].firstarc,*q;
while(p){//銷燬結點所有弧
if(p->Info){
free(p->Info);
p->Info=NULL;
}
q=p->nextarc;
free(p);
p=q;
G.arcnum--; //弧數減少
}
G.vertices[i].data=NULL; //結點賦空
for(j=0;j<G.vexnum;j++){ //遍歷每個結點 檢視是否有指向v的弧 有就銷燬
if(G.vertices[j].data){ //結點不為空
p=G.vertices[j].firstarc;
if(p&&p->adjvex==i){//第一個結點就指向v
G.vertices[j].firstarc=p->nextarc;
if(p->Info) {
free(p->Info);
p->Info=NULL;
}
free(p);
p=NULL;
G.arcnum--;
}
while(p&&p->nextarc){
q=p->nextarc;
if(q->adjvex==i) {
if(q->Info) {
free(q->Info);
q->Info=NULL;
}
p->nextarc=q->nextarc;
free(q);
q=NULL;
G.arcnum--;
}
else
p=q;
}
}
}
return OK;
}
向G中新增一個弧v,w 如果圖是無向的,還應該新增w,v.
Status InsertArc(ALGraph &G,VertexType v,VertexType w){
//向G中新增一個弧v,w 如果圖是無向的,還應該新增w,v
int i=LocateVex(G,v),j=LocateVex(G,w),l;
char s[MAX_INFO];
ArcNode *p,*q;
if(!(p=(ArcNode *)malloc(sizeof(ArcNode)))) //建立新弧結點
exit(OVERFLOW);
p->adj=1.0; //初始化
p->Info=NULL;
p->adjvex=j;
p->nextarc=NULL;
if(G.kind==UDG||G.kind==UDN){//如果是網 新增備註資訊
scanf("%lf %s",&p->adj,s);
l=strlen(s);
if(!(p->Info=(InfoType *)malloc((l+1)*sizeof(InfoType))))
exit(OVERFLOW);
strcpy(p->Info,s);
}
p->nextarc=G.vertices[i].firstarc;
G.vertices[i].firstarc=p;
G.arcnum++; //弧數增加
if(G.kind==DN||G.kind==UDN){//是無向的 新增對稱弧
if(!(q=(ArcNode *)malloc(sizeof(ArcNode))))
exit(OVERFLOW);
q->adj=1;
q->Info=NULL;
q->adjvex=i;
q->nextarc=NULL;
if(G.kind==UDN){
q->adj=p->adj;
if(!(q->Info=(InfoType *)malloc((l+1)*sizeof(InfoType))))
exit(OVERFLOW);
strcpy(q->Info,s);
}
q->nextarc=G.vertices[j].firstarc;
G.vertices[j].firstarc=q;
G.arcnum++;
}
return OK;
}
刪除圖G中弧v,w 如果圖是無向的 還應該刪除w,v.
Status DeleteArc(ALGraph &G,VertexType v,VertexType w){
//刪除圖G中弧v,w 如果圖是無向的 還應該刪除w,v
int i=LocateVex(G,v),j=LocateVex(G,w);
ArcNode *p=G.vertices[i].firstarc,*q;
if(p&&p->adjvex==j){//如果第一個弧結點是指向w
if(p->Info){
free(p->Info);
p->Info=NULL;
}
G.vertices[i].firstarc=p->nextarc;
free(p);
p=NULL;
G.arcnum--; //弧數減少
}
while(p&&p->nextarc){
q=p->nextarc;
if(q->adjvex==j){
if(q->Info){
free(q->Info);
q->Info=NULL;
}
p->nextarc=q->nextarc;
free(q);
q=NULL;
G.arcnum--;
}
else
p=q;
}
if(G.kind==DN||G.kind==UDN){ //無向的 銷燬對稱弧
p=G.vertices[j].firstarc;
if(p&&p->adjvex==i){
if(p->Info){
free(p->Info);
p->Info=NULL;
}
G.vertices[j].firstarc=p->nextarc;
free(p);
p=NULL;
G.arcnum--;
}
while(p&&p->nextarc){
q=p->nextarc;
if(q->adjvex==i){
if(q->Info){
free(q->Info);
q->Info=NULL;
}
p->nextarc=q->nextarc;
free(q);
q=NULL;
G.arcnum--;
}
else
p=q;
}
}
return OK;
}
為G中v頂點賦值為value.
Status PutVex(ALGraph &G,VertexType v,VertexType value){
//為G中v頂點賦值為value
int i=LocateVex(G,v);
G.vertices[i].data=value;
return OK;
}
返回G中頂點v的第一個鄰接點,如果沒有返回空 由於是逆序排列 所以找最後一個弧結點.
int FirstAdjVex(ALGraph G,int v){
//返回G中頂點v的第一個鄰接點,如果沒有返回空
//由於是逆序排列 所以找最後一個弧結點
ArcNode *p=G.vertices[v].firstarc;
while(p->nextarc)
p=p->nextarc;
if(p)
return p->adjvex;
else
return -1;
}
w是G中頂點v的一個鄰接點 返回v的鄰接點w後下一個鄰接點,若沒有返回空.由於是逆序排列 所以找指向w的結點的前驅弧結點所指的結點.
int NextAdjVex(ALGraph &G,int v,int w){
//w是G中頂點v的一個鄰接點 返回v的鄰接點w後下一個鄰接點,若沒有返回空
//由於是逆序排列 所以找指向w的結點的前驅弧結點所指的結點
ArcNode *p=G.vertices[v].firstarc;
while(p->nextarc){
if(p->nextarc->adjvex==w)
return p->adjvex;
p=p->nextarc;
}
return -1;
}
輸出圖G的結點數 弧數 圖型別 以及所有頂點和其對應的弧.
void PrintGraph(ALGraph G){
//輸出圖G的結點數 弧數 圖型別 以及所有頂點和其對應的弧
int i,n=G.vexnum;
for(i=0;i<G.vexnum;i++)
if(!G.vertices[i].data) //n是圖中實際的結點數
n--;
if(!n)
printf("空圖\n");
else{
printf("結點數:%d 弧數:%d ",n,G.arcnum);
switch(G.kind)
{
case DG:
printf("有向圖\n");
break;
case DN:
printf("無向圖\n");
break;
case UDG:
printf("有向網\n");
break;
case UDN:
printf("無向網\n");
break;
}
printf("結點:");
for(i=0;i<G.vexnum;i++)
if(G.vertices[i].data) //各個結點
printf("%c ",G.vertices[i].data);
printf("\n");
for(i=0;i<G.vexnum;i++) //各個弧{
if(G.vertices[i].data){
ArcNode *p=G.vertices[i].firstarc;
while(p){
printf("%c %c %lf %s\n",G.vertices[i].data,G.vertices[p->adjvex].data,p->adj,p->Info);
p=p->nextarc;
}
}
}
}