您的位置:首页 > 其它

存储在图的形式——邻接列表

2015-07-29 14:00 274 查看
邻接表:邻接表是图的一种链式存储结构。在邻接表中,对图中每一个顶点建立一个单链表,第i个单链表中的节点表示依附于顶点vi的边(对有向图是以顶点vi为尾的弧)。每一个结点有三个域组成,当中邻接点域指示与顶点vi邻接的点在途中的位置,链域指示下一条边或者弧的结点;数据域存储和边或者弧相关的信息。如权值等。每一个链表上附设一个表头结点。

在表头结点中。除了设置链域指向链表第一个结点之外,还设置有存储顶点vi的名。例如以下所看到的:



实现:

/**************************************
图的存储之邻接表
by Rowandjj
2014/6/23
**************************************/

#include<iostream>
using namespace std;

#define MAX_VERTEX_NUM 20//最大顶点数

typedef enum{DG,DN,AG,AN}GraphKind;//有向图、有向网、无向图、无向网
typedef struct _ARCNODE_//表节点(弧)
{
int adjvex;//邻接点序号
struct _ARCNODE_ *nextarc;//指向下一条弧
int info;//信息(权值)
}ArcNode;

typedef struct _VNODE_//头结点
{
char data;//顶点名
ArcNode *firstarc;//指向第一条弧
}VNode,AdjList[MAX_VERTEX_NUM];

typedef struct _ALGRAPH_//邻接表
{
AdjList vertices;//邻接表
int vexnum;//顶点数
int arcnum;//弧数
GraphKind kind;//图的种类
}ALGraph;

void (*VisitFunc)(char);  //全局函数指针

bool visited[MAX_VERTEX_NUM]; /* 訪问标志数组(全局量) */

void Visit(char p)
{
cout<<p<<" ";
}
//-----------------操作-------------------------------------
int LocateVex(ALGraph G,char u);//若G中存在顶点u,则返回该顶点在图中位置;否则返回-1
bool CreateGraph(ALGraph* G);//採用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图)
void DestroyGraph(ALGraph* G);//销毁图G
char GetVex(ALGraph G,int v);//通过序号v得到顶点名
bool PutVex(ALGraph* G,char v,char value);//对v赋新值value
int FirstAdjVex(ALGraph G,char v);//返回顶点v的第一个邻接顶点的序号
int NextAdjVex(ALGraph G,char v,char w);//返回v的(相对于w的)下一个邻接顶点的序号,若w是v的最后一个邻接点,则返回-1
void InsertVex(ALGraph* G,char v);//在图G中增添新顶点v(不增添与顶点相关的弧,留待InsertArc()去做)
bool DeleteVex(ALGraph* G,char v);//删除G中顶点v及其相关的弧
bool InsertArc(ALGraph* G,char v,char w);//在G中增添弧<v,w>,若G是无向的,则还增添对称弧<w,v>
bool DeleteArc(ALGraph* G,char v,char w);//在G中删除弧<v,w>,若G是无向的,则还删除对称弧<w,v>
void DFSTravel(ALGraph* G,void (*Visit)(char));//深度优先
void DFS(ALGraph G,int v);
void BFSTravel(ALGraph G,void (*Visit)(char));//广度优先
void Display(ALGraph G);//打印图

//----------------辅助队列------------------------------------------
#define MAX_QUEUE_SIZE 20
typedef struct _QUEUENODE_
{
int data;
struct _QUEUENODE_ *next;
}QueueNode;
typedef struct _QUEUE_
{
QueueNode *pHead;
QueueNode *pTail;
int size;
}Queue;

bool InitQueue(Queue *Q);
bool DestroyQueue(Queue *Q);
bool DeQueue(Queue *Q,int* e);
bool EnQueue(Queue *Q, int e);
bool QueueEmpty(Queue Q);
//------------------------------------------------------------------
bool InitQueue(Queue *Q)
{
Q->pHead = Q->pTail = (QueueNode *)malloc(sizeof(QueueNode));
if(!Q->pHead)
{
return false;
}
Q->pHead->next = NULL;
Q->size = 0;
return true;
}

bool EnQueue(Queue *Q, int e)
{
QueueNode *node = (QueueNode*)malloc(sizeof(QueueNode));
node->data = e;
node->next = NULL;
Q->pTail->next = node;
Q->pTail = node;
Q->size++;
return true;
}

bool DeQueue(Queue *Q,int* e)
{
QueueNode *node = Q->pHead->next;
if(node)
{
*e = node->data;
Q->pHead->next = node->next;
if(Q->pTail == node)
{
Q->pTail = Q->pHead;
}
free(node);

Q->size--;
}
return true;
}
bool QueueEmpty(Queue Q)
{
return Q.size == 0;
}
bool DestroyQueue(Queue *Q)
{
QueueNode *pTemp = Q->pHead->next;
while(pTemp != NULL)
{
Q->pHead->next = pTemp->next;
free(pTemp);
pTemp = Q->pHead->next;
}
free(Q->pHead);
Q->size = 0;
return true;
}

//------------------------------------------------------------------
int LocateVex(ALGraph G,char u)
{
int i;
for(i = 0; i < G.vexnum; i++)
{
if(u == G.vertices[i].data)
{
return i;
}
}
return -1;
}

bool CreateGraph(ALGraph* G)
{
int i,j,k;
int w;//权值
char va,vb;//弧尾、弧头
ArcNode *p;//弧

cout<<"请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): ";
scanf("%d",&(*G).kind);
cout<<"请输入图的顶点数,边数: ";
cin>>G->vexnum;
cin>>G->arcnum;

cout<<"请输入顶点值:"<<endl;
//构造顶点
for(i = 0; i < G->vexnum; i++)
{
cin>>G->vertices[i].data;
G->vertices[i].firstarc = NULL;
}
if(G->kind == 1 || G->kind == 3)//网
{
cout<<"请顺序输入每条弧(边)的权值、弧尾和弧头:\n";
}else//图
{
cout<<"请顺序输入每条弧(边)的弧尾和弧头\n";
}
//构造表节点链表
for(k = 0; k < G->arcnum; k++)
{
if(G->kind == 1 || G->kind == 3)//网
{
cin>>w;
cin>>va;
cin>>vb;
}else//图
{
cin>>va;
cin>>vb;
}
//定位弧尾弧头的位置
i = LocateVex(*G,va);
j = LocateVex(*G,vb);

p = (ArcNode *)malloc(sizeof(ArcNode));
p->adjvex = j;

if(G->kind == 1 || G->kind == 3)//网
{
p->info = w;//权值
}else
{
p->info = NULL;
}
//插入表
p->nextarc = G->vertices[i].firstarc;//插在表头
G->vertices[i].firstarc = p;

//假设是无向图或者无向网。还须要添加对称结点
if(G->kind == 2 || G->kind == 3)
{
p = (ArcNode *)malloc(sizeof(ArcNode));
p->adjvex = i;

if(G->kind == 3)//若是无向网。还须要权值
{
p->info = w;
}else
{
p->info = NULL;
}

//插入表
p->nextarc = G->vertices[j].firstarc;
G->vertices[j].firstarc = p;
}
}
return true;
}

void Display(ALGraph G)
{
ArcNode *p;
int i;
switch(G.kind)
{
case DG:
cout<<"有向图";
break;
case AG:
cout<<"无向图";
break;
case DN:
cout<<"有向网";
break;
case AN:
cout<<"无向网";
break;
default:
break;
}
cout<<endl;
cout<<"顶点:"<<endl;
for(i = 0; i < G.vexnum; i++)
{
cout<<G.vertices[i].data<<" ";
}
cout<<endl;
//边
cout<<"边:"<<endl;
for(i = 0; i < G.vexnum; i++)
{
p = G.vertices[i].firstarc;
while(p)
{
if(G.kind == 0 || G.kind == 1)//有向
{
cout<<G.vertices[i].data<<" "<<G.vertices[p->adjvex].data;
if(G.kind == 1)//有向网
{
cout<<" "<<p->info;
}

}else//无向
{
if(i < p->adjvex)//不反复打印
{
cout<<G.vertices[i].data<<" "<<G.vertices[p->adjvex].data;
if(G.kind == 3)//无向网
{
cout<<" "<<p->info;
}
}
}
cout<<endl;
p = p->nextarc;
}
}

}
void DestroyGraph(ALGraph* G)
{
ArcNode *p,*q;
int i;

for(i = 0; i < G->vexnum; i++)
{
p = G->vertices[i].firstarc;
while(p)
{
q = p->nextarc;
free(p);
p = q;
}
}
G->arcnum = 0;
G->vexnum = 0;
}
char GetVex(ALGraph G,int v)
{
if(v>=G.vexnum || v<0)
{
exit(0);
}
return G.vertices[v].data;
}
bool PutVex(ALGraph* G,char v,char value)
{
int i = LocateVex(*G,v);
if(i == -1)
{
return false;
}
G->vertices[i].data = value;

return true;
}
int FirstAdjVex(ALGraph G,char v)
{
int i = LocateVex(G,v);
if(i < 0)
{
return -1;
}
ArcNode *arcNode = G.vertices[i].firstarc;
if(arcNode == NULL)
{
return -1;
}
return arcNode->adjvex;
}
int NextAdjVex(ALGraph G,char v,char w)
{
int i,j;
i = LocateVex(G,v);
j = LocateVex(G,w);
ArcNode *p = G.vertices[i].firstarc;
while(p && p->adjvex != j)
{
p = p->nextarc;
}
if(!p || !p->nextarc)//没找到w或w是最后一个邻接点
{
return -1;
}
else
{
return p->nextarc->adjvex;
}
}
void InsertVex(ALGraph* G,char v)
{
G->vertices[G->vexnum].data = v;
G->vertices[G->vexnum].firstarc = NULL;

G->vexnum++;
}
bool DeleteVex(ALGraph* G,char v)
{
int i,j;
ArcNode *p,*q;
//1.删除邻接表中顶点为v的那一行全部数据,更改弧总数,顶点总数
i = LocateVex(*G,v);
if(i < 0 || i >= G->vexnum)//不合法的位置
{
return false;
}
p = G->vertices[i].firstarc;
while(p)//依次删除弧
{
q = p->nextarc;
free(p);
p = q;
G->arcnum--;
}
G->vexnum--;
//2.更改顶点v之后的顶点在数组中的位置(前移一位)
for(j = i; j < G->vexnum; j++)
{
G->vertices[j] = G->vertices[j+1];
}
//3.遍历剩下的邻接表,找到包括顶点v的弧或者边。删除之。另外须要注意,对遍历的每一个弧/边,视情况更新序号
for(j = 0; j < G->vexnum; j++)
{
p = G->vertices[j].firstarc;//p指向遍历的顶点的第一条弧或者边
while(p)
{
if(p->adjvex == i)//假设找到指向已删除顶点的弧或者边
{
if(p == G->vertices[j].firstarc)//假设待删除的结点是第一个结点
{
G->vertices[j].firstarc = p->nextarc;
free(p);
p = G->vertices[j].firstarc;
if(G->kind <= 1)//假设是有向的,则还需更改弧数
{
G->arcnum--;
}
}else//不是第一个结点
{
q->nextarc = p->nextarc;
free(p);
p = q->nextarc;
if(G->kind <= 1)//假设是有向的,则还需更改弧数
{
G->arcnum--;
}
}
}else//假设当前弧并非要找的弧,那么继续向后遍历
{
if(p->adjvex > i)//(非常关键)更新序号
{
p->adjvex--;
}
q = p;
p = p->nextarc;//指向下一条弧
}
}
}
return true;
}
bool InsertArc(ALGraph* G,char v,char w)
{
int i,j,weight;
ArcNode *arcNode;
//1.得到v、w的在邻接表中的序号
i = LocateVex(*G,v);
j = LocateVex(*G,w);
if(i<0 || j<0)
{
return false;
}
G->arcnum++;
if(G->kind == 1 || G->kind == 3)
{
cout<<"输入权值:";
cin>>weight;//输入权值
}

//2.生成一个弧结点,插入到顶点v的第一个邻接点的位置(假设是网的话,须要用户输入权值)
arcNode = (ArcNode*)malloc(sizeof(ArcNode));
arcNode->adjvex = j;
if(G->kind == 1 || G->kind == 3)
{
arcNode->info = weight;
}
else
{
arcNode->info = NULL;
}

arcNode->nextarc = G->vertices[i].firstarc;
G->vertices[i].firstarc = arcNode;
//3.假设是无向的,那么还需生成对称节点,并插到合适位置
if(G->kind >= 2)
{
arcNode = (ArcNode *)malloc(sizeof(ArcNode));
arcNode->adjvex = i;
if(G->kind == 3)//无向网
{
arcNode->info = weight;
}
else
{
arcNode->info = NULL;
}
arcNode->nextarc = G->vertices[j].firstarc;
G->vertices[j].firstarc = arcNode;
}

return true;
}
bool DeleteArc(ALGraph* G,char v,char w)
{
int i,j;
ArcNode *p,*q;
//1.得到v、w的在邻接表中的序号
i = LocateVex(*G,v);
j = LocateVex(*G,w);
if(i < 0 || j < 0)
{
return false;
}
//2.删除v-w
p = G->vertices[i].firstarc;
while(p && p->adjvex!=j)
{
q = p;
p = p->nextarc;
}
if(p && p->adjvex==j)//找到弧<v-w>
{
if(p == G->vertices[i].firstarc)//p指的是第一条弧
{
G->vertices[i].firstarc = p->nextarc;
}
else
{
q->nextarc = p->nextarc;
}
free(p);
G->arcnum--;
}

//3.若是无向,则还删除w-v
if(G->kind >= 2)
{
p = G->vertices[j].firstarc;
while(p && p->adjvex!=i)
{
q = p;
p = p->nextarc;
}
if(p && p->adjvex==i)//找到弧<w-v>
{
if(p == G->vertices[j].firstarc)//p指的是第一条弧
{
G->vertices[j].firstarc = p->nextarc;
}
else
{
q->nextarc = p->nextarc;
}
free(p);
}
}
return true;
}
void DFSTravel(ALGraph* G,void (*Visit)(char))
{
int i;
VisitFunc = Visit;
for(i = 0; i < G->vexnum; i++)
{
visited[i] = false;
}
for(i = 0; i < G->vexnum; i++)
{
if(!visited[i])
{
DFS(*G,i);
}
}
cout<<endl;
}
void DFS(ALGraph G,int v)
{
int i;
char v1,w1;
v1 = GetVex(G,v);
visited[v] = true;
VisitFunc(G.vertices[v].data);

for(i = FirstAdjVex(G,v1);i>=0; i = NextAdjVex(G,v1,w1 = GetVex(G,i)))
{
if(!visited[i])
{
DFS(G,i);
}
}
}
void BFSTravel(ALGraph G,void (*Visit)(char))
{
Queue q;
InitQueue(&q);
char w1,u1;
int i,u,w;
for(i = 0; i < G.vexnum; i++)
{
visited[i] = false;
}

for(i = 0; i < G.vexnum; i++)
{
if(!visited[i])
{
visited[i] = true;
Visit(G.vertices[i].data);
EnQueue(&q,i);

while(!QueueEmpty(q))
{
DeQueue(&q,&u);
u1 = GetVex(G,u);
for(w = FirstAdjVex(G,u1);w>=0;w = NextAdjVex(G,u1,w1=GetVex(G,w)))
{
if(!visited[w])
{
visited[w] = true;
Visit(G.vertices[w].data);
EnQueue(&q,w);
}

}
}
}
}
DestroyQueue(&q);
cout<<endl;
}
int main()
{

ALGraph graph;
CreateGraph(&graph);
Display(graph);

cout<<"深度优先:"<<endl;
DFSTravel(&graph,Visit);
cout<<"广度优先:"<<endl;
BFSTravel(graph,Visit);
DestroyGraph(&graph);

return 0;
}


測试:

考虑下面有向图:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: