文章目录

一、 邻接矩阵

二、 邻接矩阵的主要特点

三、邻接链表

四、 邻接矩阵的建立

五、 邻接链表的建立

一、邻接矩阵

用两个数组来表示图。一个一位数组存储图中顶点信息, 一个二维数组(邻接矩阵)存储图中的边或弧的信息。

举例:无向图和有向图的对比

  • 无向图邻接矩阵对称
  • 但有向图的邻接矩阵不一定都不对称

二、邻接矩阵的主要特点:

  • 一个图的邻接矩阵表示是唯一的
  • 适合于稠密图的存储
  • 存储空间为O(N^2)
  • 邻接矩阵的好处:
无向图:

有向图:

网:需要存权值(W)
这里“∞”表示一个计算机允许的、大于所有边上权值的值。 是一个不可能的极限值


四、邻接矩阵的建立

无向网图邻接矩阵的建立:


#define MAXWEX 100 //最大顶点数,自定义 
#define INFINITY 65535 //用65535表示∞
typedef struct
{
   
	char vesx[MAXWEX];  //顶点表
	int arc[MAXWEX][MAXWEX];  //邻接矩阵(边表)
	int numv;  //顶点数
	int nume;  //边数
}MGraph;
void CreateMGraph(MGraph* G)
{
   
	int i, j, k, w;
	printf("请分别输入顶点数和边数:\n");
	scanf("%d %d",&G->numv,&G->nume);
	//读入顶点信息
	for (i = 0; i < G->numv; i++)
	{
   
		scanf("%c",&G->vesx[i]);
	}
	
	for (i = 0; i < G->nume; i++)
	{
   
		for (j = 0; j < G->nume; j++)
		{
   
			G->arc[i][j] = INFINITY;  //邻接矩阵初始化为∞
		}	
	}
	//建立邻接矩阵
	for (k = 0; k < G->nume; k++)
	{
   
		printf("请输入边(Vi,Vj)的下标i,j,和权值w:\n");
		scanf("%d %d %d", &i, &j, &w);
		G->arc[i][j] = w;
		G->arc[j][i] = G->arc[i][j];  //无向图矩阵对称
	}
}

邻接矩阵的综合建立:

/* 图的邻接矩阵表示法 */
 
#define MaxVertexNum 100 /* 最大顶点数设为100 */
#define INFINITY 65535 /* ∞设为双字节无符号整数的最大值65535*/
typedef int Vertex;         /* 用顶点下标表示顶点,为整型 */
typedef int WeightType;        /* 边的权值设为整型 */
typedef char DataType;        /* 顶点存储的数据类型设为字符型 */
 
/* 边的定义 */
typedef struct ENode *PtrToENode;
struct ENode{
   
    Vertex V1, V2;      /* 有向边<V1, V2> */
    WeightType Weight;  /* 权重 */
};
typedef PtrToENode Edge;
        
/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{
   
    int Nv;  /* 顶点数 */
    int Ne;  /* 边数 */
    WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵 */
    DataType Data[MaxVertexNum];      /* 存顶点的数据 */
    /* 注意:很多情况下,顶点无数据,此时Data[]可以不用出现 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型 */
 
 
 
MGraph CreateGraph( int VertexNum )
{
    /* 初始化一个有VertexNum个顶点但没有边的图 */
    Vertex V, W;
    MGraph Graph;
     
    Graph = (MGraph)malloc(sizeof(struct GNode)); /* 建立图 */
    Graph->Nv = VertexNum;
    Graph->Ne = 0;
    /* 初始化邻接矩阵 */
    /* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */
    for (V=0; V<Graph->Nv; V++)
        for (W=0; W<Graph->Nv; W++)  
            Graph->G[V][W] = INFINITY;
             
    return Graph; 
}
        
void InsertEdge( MGraph Graph, Edge E )
{
   
     /* 插入边 <V1, V2> */
     Graph->G[E->V1][E->V2] = E->Weight;    
     /* 若是无向图,还要插入边<V2, V1> */
     Graph->G[E->V2][E->V1] = E->Weight;
}
 
MGraph BuildGraph()
{
   
    MGraph Graph;
    Edge E;
    Vertex V;
    int Nv, i;
     
    scanf("%d", &Nv);   /* 读入顶点个数 */
    Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */ 
     
    scanf("%d", &(Graph->Ne));   /* 读入边数 */
    if ( Graph->Ne != 0 ) {
    /* 如果有边 */ 
        E = (Edge)malloc(sizeof(struct ENode)); /* 建立边结点 */ 
        /* 读入边,格式为"起点 终点 权重",插入邻接矩阵 */
        for (i=0; i<Graph->Ne; i++) {
   
            scanf("%d %d %d", &E->V1, &E->V2, &E->Weight); 
            /* 注意:如果权重不是整型,Weight的读入格式要改 */
            InsertEdge( Graph, E );
        }
    } 
 
    /* 如果顶点有数据的话,读入数据 */
    for (V=0; V<Graph->Nv; V++) 
        scanf(" %c", &(Graph->Data[V]));
 
    return Graph;
}

三、邻接链表:数组与链表相结合

邻接表基本概念:

  • N个头指针+2E个结点(每个结点至少2个域)
    方便计算度吗?
  • 对于无向图:方便计算度
  • 对于有向图:只能计算初度,需要构造逆邻接表(存指向自己的边)来方便计算入度
  • 不方便检查任意一对顶点存在边

邻接表特点:

  • 邻接表表示不唯一

  • 适合于稀疏图存储
  • 存储空间O(n+e)

无向图的邻接表

有向图的邻接表

五、邻接链表的建立

无向图邻接表:
//边表结点
typedef struct EdgeNode
{
   
	int adjvex;  //该顶点对应的下标
	int weight;  //权值,非网图不需要
	struct EdgeNode* next; //指向下一个邻接点
}EdgeNode;

//顶点表结点
typedef struct VertexNode
{
   
	char data; //存储顶点信息
	EdgeNode* fistedge; //边表头指针
}VertexNode,AdjList[MAXWEX];

typedef struct
{
   
	AdjList adjList;  //顶点表
	int nume;  //图中当前边数
	int numv;  //图中当前顶点数
}GraphAdjList;
void CreateALGraph(GraphAdjList* G)
{
   
	int i, j, k;
	EdgeNode* e; //边表结点
	printf("请分别输入顶点数和边数:\n");//若有权值,可在加入 w
	scanf("%d %d",&G->numv,&G->nume);
	//读入顶点信息
	for (i = 0; i < G->numv; i++)
	{
   
		scanf("%c", &G->adjList[i].data); //输入顶点信息
		G->adjList[i].fistedge = NULL;    //边表置为空表
	}

	//建立边表
	for (k = 0; k < G->nume; k++)
	{
   
		printf("请输入边(Vi,Vj)上的顶点序号:\n");
		scanf("%d %d", &i, &j);

		//类似于头插法
		e = (EdgeNode*)malloc(sizeof(EdgeNode));
		e->adjvex = j;   //邻接序号为 j -> i 指向的顶点的下标
		//e->weight = w;//存储该边的权值
		e->next = G->adjList[i].fistedge;  //将 e 的指针指向当前顶点指向的结点
		G->adjList[i].fistedge = e;   //将当前顶点是指针指向 e
		//对于无向图而言,对称的
		e = (EdgeNode*)malloc(sizeof(EdgeNode));
		e->adjvex = i;    //邻接序号为 i -> j 指向的顶点的下标
		//e->weight = w;//存储该边的权值
		e->next = G->adjList[j].fistedge;  //将 e 的指针指向当前顶点指向的结点
		G->adjList[j].fistedge = e;   //将当前顶点是指针指向 e
	}
}

 
邻接表的综合建立:
/* 图的邻接表表示法 */
 
#define MaxVertexNum 100 /* 最大顶点数设为100 */
typedef int Vertex;         /* 用顶点下标表示顶点,为整型 */
typedef int WeightType;        /* 边的权值设为整型 */
typedef char DataType;        /* 顶点存储的数据类型设为字符型 */
 
/* 边的定义 */
typedef struct ENode *PtrToENode;
struct ENode{
   
    Vertex V1, V2;      /* 有向边<V1, V2> */
    WeightType Weight;  /* 权重 */
};
typedef PtrToENode Edge;
 
/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode; 
struct AdjVNode{
   
    Vertex AdjV;        /* 邻接点下标 */
    WeightType Weight;  /* 边权重 */
    PtrToAdjVNode Next;    /* 指向下一个邻接点的指针 */
};
 
/* 顶点表头结点的定义 */
typedef struct Vnode{
   
    PtrToAdjVNode FirstEdge;/* 边表头指针 */
    DataType Data;            /* 存顶点的数据 */
    /* 注意:很多情况下,顶点无数据,此时Data可以不用出现 */
} AdjList[MaxVertexNum];    /* AdjList是邻接表类型 */
 
/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{
     
    int Nv;     /* 顶点数 */
    int Ne;     /* 边数 */
    AdjList G;  /* 邻接表 */
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */
 
 
 
LGraph CreateGraph( int VertexNum )
{
    /* 初始化一个有VertexNum个顶点但没有边的图 */
    Vertex V;
    LGraph Graph;
     
    Graph = (LGraph)malloc( sizeof(struct GNode) ); /* 建立图 */
    Graph->Nv = VertexNum;
    Graph->Ne = 0;
    /* 初始化邻接表头指针 */
    /* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */
       for (V=0; V<Graph->Nv; V++)
        Graph->G[V].FirstEdge = NULL;
             
    return Graph; 
}
        
void InsertEdge( LGraph Graph, Edge E )
{
   
    PtrToAdjVNode NewNode;
     
    /* 插入边 <V1, V2> */
    /* 为V2建立新的邻接点 */
    NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
    NewNode->AdjV = E->V2;
    NewNode->Weight = E->Weight;
    /* 将V2插入V1的表头 */
    NewNode->Next = Graph->G[E->V1].FirstEdge;
    Graph->G[E->V1].FirstEdge = NewNode;
         
    /* 若是无向图,还要插入边 <V2, V1> */
    /* 为V1建立新的邻接点 */
    NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
    NewNode->AdjV = E->V1;
    NewNode->Weight = E->Weight;
    /* 将V1插入V2的表头 */
    NewNode->Next = Graph->G[E->V2].FirstEdge;
    Graph->G[E->V2].FirstEdge = NewNode;
}
 
LGraph BuildGraph()
{
   
    LGraph Graph;
    Edge E;
    Vertex V;
    int Nv, i;
     
    scanf("%d", &Nv);   /* 读入顶点个数 */
    Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */ 
     
    scanf("%d", &(Graph->Ne));   /* 读入边数 */
    if ( Graph->Ne != 0 ) {
    /* 如果有边 */ 
        E = (Edge)malloc( sizeof(struct ENode) ); /* 建立边结点 */ 
        /* 读入边,格式为"起点 终点 权重",插入邻接矩阵 */
        for (i=0; i<Graph->Ne; i++) {
   
            scanf("%d %d %d", &E->V1, &E->V2, &E->Weight); 
            /* 注意:如果权重不是整型,Weight的读入格式要改 */
            InsertEdge( Graph, E );
        }
    } 
 
    /* 如果顶点有数据的话,读入数据 */
    for (V=0; V<Graph->Nv; V++) 
        scanf(" %c", &(Graph->G[V].Data));
 
    return Graph;
}
邻接表的遍历:

//不能传指针,防止遍历时改变原来结构
void PrintGraphAdjList(GraphAdjList G)
{
   
	for (int i = 0; i < G.numv; i++)
	{
   
		EdgeNode* p = G.adjList[i].fistedge;
		printf("该顶点为 %c:\n", G.adjList[i].data);
		while (p)
		{
   
			printf("(%c,%c),权值为 %d \n", G.adjList[i].data, G.adjList[p->adjvex].data, p->weight);
			p = p->next;
		}
		printf("\n");
	}
}