1. 程式人生 > >資料結構-圖-鄰接表

資料結構-圖-鄰接表

使用鄰接矩陣有它的優點:易於求結點度,求鄰接點,易判斷兩點間是否有弧相連。但不利於稀疏圖的儲存,因弧不存在時也要儲存相應資訊。且要預分配足夠大空間。

下面來介紹使用鄰接表的方式來儲存圖。

在鄰接表中,對圖中每個頂點建立一個單鏈表,第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;
	       }
           }
       }
    }