一. 图的简介
1. 无向图
无向图.png
-
邻接矩阵
上图是一个无向图,我们使用邻接矩阵可以来描述一个无向图中顶点和边的关系。以上图为例,顶点数组为[v0, v1, v2, v3]
,在邻接矩阵中,两个顶点之间有边则用1表示,无边则用0表示,那么上图的邻接矩阵表示如下:
-
邻接表
上面的无向图可以使用邻接表来存储。使用一个一位数组来存储顶点节点,每个顶点指针域指向和这个顶点有连接关系的结点,如下图:
2. 带权值的有向图
图.png
-
邻接矩阵
在邻接矩阵中描述一个有向图,对角线顶点自身的关系用0表示,两个顶点之间有连接则存储边的权值,无连接用∞表示,如下图:
-
邻接表
带权值的有向图,使用邻接表表示时,需要在邻接表节点中加一个数据域来存储权值。如下图:
二. 图的存储
- 邻接矩阵
1. 图的邻接矩阵结构体设计
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 /* 最大顶点数,应由用户定义 */
#define INFINITYC 0
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef char VertexType; /* 顶点类型应由用户定义 */
typedef int EdgeType; /* 边上的权值类型应由用户定义 */
typedef struct
{
VertexType vexs[MAXVEX]; /* 顶点表 */
EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
int numNodes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;
2. 创建图的邻接矩阵
void CreateMGraph(MGraph *G){
int i,j,k,w;
printf("输入顶点数和边数:\n");
//1. 输入顶点数/边数
scanf("%d,%d",&G->numNodes,&G->numEdges);
printf("顶点数:%d,边数:%d\n",G->numNodes,G->numEdges);
//2.输入顶点信息/顶点表
for(i = 0; i<= G->numNodes;i++)
scanf("%c",&G->vexs[I]);
//3.初始化邻接矩阵
for(i = 0; i < G->numNodes;i++)
for(j = 0; j < G->numNodes;j++)
G->arc[i][j] = INFINITYC;
//4.输入边表信息
for(k = 0; k < G->numEdges;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];
}
/*5.打印邻接矩阵*/
for (int i = 0; i < G->numNodes; i++) {
printf("\n");
for (int j = 0; j < G->numNodes; j++) {
printf("%d ",G->arc[i][j]);
}
}
printf("\n");
}
- 邻接表
1. 图的邻接表存储节点设计
#define M 100
#define true 1
#define false 0
typedef char Element;
typedef int BOOL;
//邻接表的节点
typedef struct Node{
int adj_vex_index; //弧头的下标,也就是被指向的下标
Element data; //权重值
struct Node * next; //边指针
}EdgeNode;
//顶点节点表
typedef struct vNode{
Element data; //顶点的权值
EdgeNode * firstedge; //顶点下一个是谁?
}VertexNode, Adjlist[M];
//总图的一些信息
typedef struct Graph{
Adjlist adjlist; //顶点表
int arc_num; //边的个数
int node_num; //节点个数
BOOL is_directed; //是不是有向图
}Graph, *GraphLink;
2. 创建图的邻接表
void creatGraph(GraphLink *g){
int i,j,k;
EdgeNode *p;
//1. 顶点,边,是否有向
printf("输入顶点数目,边数和有向?:\n");
scanf("%d %d %d", &(*g)->node_num, &(*g)->arc_num, &(*g)->is_directed);
//2.顶点表
printf("输入顶点信息:\n");
for (i = 0; i < (*g)->node_num; i++) {
getchar();
scanf("%c", &(*g)->adjlist[i].data);
(*g)->adjlist[i].firstedge = NULL;
}
//3.
printf("输入边信息:\n");
for (k = 0; k < (*g)->arc_num; k++){
getchar();
scanf("%d %d", &i, &j);
//①新建一个节点
p = (EdgeNode *)malloc(sizeof(EdgeNode));
//②弧头的下标
p->adj_vex_index = j;
//③头插法插进去,插的时候要找到弧尾,那就是顶点数组的下标i
p->next = (*g)->adjlist[i].firstedge;
//④将顶点数组[i].firstedge 设置为p
(*g)->adjlist[i].firstedge = p;
//j->i
if(!(*g)->is_directed)
{
// j -----> i
//①新建一个节点
p = (EdgeNode *)malloc(sizeof(EdgeNode));
//②弧头的下标i
p->adj_vex_index = i;
//③头插法插进去,插的时候要找到弧尾,那就是顶点数组的下标i
p->next = (*g)->adjlist[j].firstedge;
//④将顶点数组[i].firstedge 设置为p
(*g)->adjlist[j].firstedge = p;
}
}
}
三. 图的遍历
1. 深度优先遍历
- 邻接矩阵
Boolean visited[MAXVEX]; /* 访问标志的数组 */
//1. 标识顶点是否被标记过;
//2. 选择从某一个顶点开始(注意:非连通图的情况)
//3. 进入递归,打印i点信息,标识; 边表
//4. [i][j] 是否等于1,没有变遍历过visted
void DFS(MGraph G,int i){
//1.
visited[i] = TRUE;
printf("%c",G.vexs[i]);
//2.0~numVertexes
for(int j = 0; j < G.numVertexes;j++){
if(G.arc[i][j] == 1 && !visited[j])
DFS(G, j);
}
}
void DFSTravese(MGraph G){
//1.初始化
for(int i=0;i<G.numVertexes;i++){
visited[i] = FALSE;
}
//2.某一个顶点
for(int i = 0;i<G.numVertexes;i++){
if(!visited[i]){
DFS(G, i);
}
}
}
- 邻接表
Boolean visited[MAXSIZE]; /* 访问标志的数组 */
/* 邻接表的深度优先递归算法 */
void DFS(GraphAdjList GL, int i)
{
EdgeNode *p;
visited[i] = TRUE;
//2.打印顶点 A
printf("%c ",GL->adjList[i].data);
p = GL->adjList[i].firstedge;
//3.
while (p) {
if(!visited[p->adjvex])
DFS(GL,p->adjvex);
p = p->next;
}
}
/* 邻接表的深度遍历操作 */
void DFSTraverse(GraphAdjList GL)
{
//1. 将访问记录数组默认置为FALSE
for (int i = 0; i < GL->numVertexes; i++) {
/*初始化所有顶点状态都是未访问过的状态*/
visited[i] = FALSE;
}
//2. 选择一个顶点开始DFS遍历. 例如A
for(int i = 0; i < GL->numVertexes; i++)
//对未访问过的顶点调用DFS, 若是连通图则只会执行一次.
if(!visited[i])
DFS(GL, i);
}
2、广度优先遍历
- 邻接矩阵
Boolean visited[MAXVEX]; /* 访问标志的数组 */
void BFSTraverse(MGraph G){
int temp = 0;
//1.
Queue Q;
InitQueue(&Q);
//2.将访问标志数组全部置为"未访问状态FALSE"
for (int i = 0 ; i < G.numVertexes; i++) {
visited[i] = FALSE;
}
//3.对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
for (int i = 0 ; i < G.numVertexes; i++) {
if(!visited[i]){
visited[i] = TRUE;
printf("%c ",G.vexs[i]);
//4. 入队
EnQueue(&Q, i);
while (!QueueEmpty(Q)) {
//出队
DeQueue(&Q, &i);
for (int j = 0; j < G.numVertexes; j++) {
if(G.arc[i][j] == 1 && !visited[j])
{ visited[j] = TRUE;
printf("%c ",G.vexs[j]);
EnQueue(&Q, j);
}
}
}
}
}
}
- 邻接表
Boolean visited[MAXSIZE]; /* 访问标志的数组 */
void BFSTraverse(GraphAdjList GL){
//1.创建结点
EdgeNode *p;
Queue Q;
InitQueue(&Q);
//2.将访问标志数组全部置为"未访问状态FALSE"
for(int i = 0; i < GL->numVertexes; i++)
visited[i] = FALSE;
//3.对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
for(int i = 0 ;i < GL->numVertexes;i++){
//4.判断当前结点是否被访问过.
if(!visited[i]){
visited[i] = TRUE;
//打印顶点
printf("%c ",GL->adjList[i].data);
EnQueue(&Q, i);
while (!QueueEmpty(Q)) {
DeQueue(&Q, &i);
p = GL->adjList[i].firstedge;
while (p) {
//判断
if(!visited[p->adjvex]){
visited[p->adjvex] = TRUE;
printf("%c ",GL->adjList[p->adjvex].data);
EnQueue(&Q, p->adjvex);
}
p = p->next;
}
}
}
}
}