数据结构2
——《大话数据结构》的搬运工
第七章
图的定义
7.2.1各种图的定义
无向边:l两顶点间无方向,用无序偶对
(
v
i
,
v
j
)
(v_i,v_j)
(vi,vj)表示
无向图:任意两顶点之间的边都是无向边,则为无向图,顶点A和D,(A,D)或(D,A)
无向图
G
1
=
(
V
1
,
E
1
)
G_1=(V_1,{E_1})
G1=(V1,E1),顶点结合
V
1
=
A
,
B
,
C
,
D
V_1={A,B,C,D}
V1=A,B,C,D,边几何
E
1
=
(
A
,
B
)
,
(
B
,
C
)
,
(
D
,
A
)
,
(
A
,
C
)
E_1={(A,B),(B,C),(D,A),(A,C)}
E1=(A,B),(B,C),(D,A),(A,C)
有向边(弧):两顶点间有方向,用有序偶对
<
v
i
,
v
j
>
<v_i,v_j>
<vi,vj>表示
有向图:任意两顶点之间的边都是有向边,则为有向图,顶点A和D,<A,D>
有向图:
G
2
=
(
V
2
,
E
2
)
G_2=(V_2,{E_2})
G2=(V2,E2),顶点结合
V
2
=
A
,
B
,
C
,
D
V_2={A,B,C,D}
V2=A,B,C,D,边几何
E
2
=
<
A
,
B
>
,
<
B
,
C
>
,
<
D
,
A
>
,
<
A
,
C
>
E_2={<A,B>,<B,C>,<D,A>,<A,C>}
E2=<A,B>,<B,C>,<D,A>,<A,C>
简单图:没有自身到自身的边
无向完全图:任意两个顶点之间都存在无向边(不是路径),n个顶点有
n
(
n
+
1
)
2
\frac{n(n+1)}{2}
2n(n+1)条边
有向完全图:任意两个顶点之间都存在有向边(互为相反的弧),n个顶点有n(n+1)条边
网:带权的图,每个边有各自的权重
7.2.2图的顶点和边间关系
无向图:
顶点的度是和顶点相关联的边的数目,TD(V)
无向图的边数是所有顶点度的和的一半
有向图:——说法有问题?
以顶点v为头的弧的数目,为V的入度,ID(V)
以顶点V为尾的弧的数目,为V的出度,OD(V)
顶点V的度为TD(V)=ID(V)+OD(V)
有向图的边数=所有顶点的入度=所有顶点的出度
无向图中某一顶点到另一个顶点的路径,为所经过顶点组成的顶点序列
有向图的路径也是有向的
回路(环):第一个顶点和最后一个顶点相同
简单回路:除最后一个顶点和第一个顶点外,路径不经过重复顶点
7.2.3连通图相关术语
连通图:图中任意两个顶点都是连通(路径)的
无向图中的极大连通子图称为连通分量:
- 子图(单纯连通子图不叫做连通分量)
- 子图是连通的
- 连通子图含有极大顶点数
- 具有极大顶点数的连通子图包含依附于这些顶点的所有边
有向图中,任意一对顶点,互有路径可以到达,称为强连通图(不是有向完全图)
有向图中的极大强连通子图,称为有向图的强连通分量
给的例子有问题吧?
连通图的生成树
极小的连通子图叫做连通图的生成树。
生成树(极小连通子图)含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边
n个顶点,小于n-1个边,是非连通图
n个顶点,大于n-1个边,必有环(不同于无向完全图)
7.4图的存储结构
7.4.1邻接矩阵
用两个数组表示图。一维数组存储图中的顶点信息。二维数组(邻接矩阵)存储图中的边的信息
n个顶点,则有一个nXn的矩阵
无向图的邻接矩阵是一个对称矩阵
网的邻接矩阵(带权的邻接矩阵):不存在的边用无穷表示
//邻接矩阵的结构
typedef char VertexType;
typedef in EdgeType;
#define MAXVEX 100
#define INFINITY 65535//用65536表示无穷
typedef struct
{
VertexType vexs[MAXVEX];//定点表
EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵
int numVertexes,numEdges;//顶点数和边数
}MGraph;
//邻接矩阵的表示
void CreateMGraph(MGraph* G)
{
int i,j,k,w;
printf("输入顶点数和边数:\n");
scanf("%d,%d",&G-numVertexes,&G->numEdges);
for(int i=0;i<G-numVertexes;i++)
{
scanf("%d",&G-vexs[i]);
}
for( i=0;i<G-numVertexes;i++)
{
for( j=0;j<G-numVertexes;j++)
{
G->arc[i][j]=INFINITY;//邻接矩阵初始化
}
}
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];//构建无向图
}
}
7.4.2邻接表
对于稀疏图,邻接矩阵浪费存储空间。
利用数组和链表相结合的存储方法称为邻接表
- 利用一维数组存储顶点,每个数据元素都存储一个指向第一个临界点的指针,便于查找顶点的边信息
- 每个顶点的所有邻接点构成一个线性表,利用单链表存储
adjvex存储的是邻接下标
有向图邻接表见P229
typedef char VertexType;//顶点类型
typedef int EdgeType;//权值类型
typedef struct EdgeNode//边表结点
{
int adjvex;//邻接点域,存储对应的下标
EdgeType weight;//用于存储权值,非网图则不需要
struct EdgeNode* next;//链域,指向下一个邻接点
}EdgeNode;
typedef struct VertexNode//顶点表结点
{
VertexType data;//存储顶点信息
EdgeNode* firstedge;//边表头结点
}VertexNode,AdjList[MAXVEX];//相当于是一个结构体数组别名
typedef struct//邻接表
{
AdjList adjList;//相当于struct VertexNode adjList[MAXVEX],每个adjList[i]都有一个data和一个firstedge;
int numVertexes,numEdges;//图中当前顶点数和边数
}GraphAdjList;
//建立图的邻接表结构
void CreateALGraph(GraphAdjList* G)//G包含一个adjList和当前顶点数和边数,一个adjList是struct VertexNode adjList[MAXVEX],每个adjList[i]都有一个data和一个firstedge;
{
int i,j,k;
EdgeNode* e;
printf("输入顶点数和边数\n");
scanf("%d,%d",&G->numVertexes,&G->numEdges);
for(i=0;i<G->numVertexes;i++)//建立顶点表
{
scanf("%c",&G->adjList[i].data);//输入顶点信息,每个adjList[i]都有一个data和一个firstedge;
G->adjList[i].firstedge=NUll;//先将边表置为空
}
//建立边表
for(k=0;k<G->numEdges;k++)
{
printf("输入边(vi,vj)上的顶点序号\n");
scanf("%d,%d",&i,&j);//输入边(vi,vj)上的顶点序号
e=(EdgeNode*)malloc(sizeof(EdgeNode));//生成边表结点
e->adjvex=j;//vi的邻接点vj邻接序号
e->next=G->adjList[i].firstedge;//有点像插入链表的意思(将e指针指向当前顶点指向的结点)
G->adjList[i].firstedge=e;//将当前顶点的指针指向e
//因为树无向图,所以i,j互为邻接
e=(EdgeNode*)malloc(sizeof(EdgeNode));//生成边表结点
e->adjvex=i;//邻接序号为i
e->next=G->adjList[j].firstedge;
G->adjList[j].firstedge=e;
}
}
注意上述插入链表的方法是头插法,边表示可以互换位置的
7.4.3十字链表
结合邻接表和逆邻接表,对有向图进行存储
//顶点表结构
data,firstin,firstout//firstin为入边表头指针,指向顶点的入边表的第一个结点,firstout表示出边表头指针,指向该顶点的出边表中的第一个结点
//边表结点结构
tailvex,headvex,headlink,taillink;//tailvex指弧起点在顶点表的下标,headvx是指弧终点在顶点表中的下标,headlink是指入表指针域,指向终点相同的下一条边,taillink指出边表指针域,指向起点相同(同一个顶点)的下一条边(出边)。若为网,则增加权重weight
书P233
7.4.4邻接多重表
无向图的邻接表更关注顶点,对于边的删除等操作比较麻烦。改进:仿照十字链表进行改造
ivex,ilink,jvex,jlink;//ivex和jvex是与某条边依附的两个顶点在顶点表中的下标,ilink指向依附顶点ivex的下一条边,jlink指向依附顶点jvex的下一条边
7.4.5边集数组
适用于对边依次进行处理的操作,见7.6.2克鲁斯卡尔kruskal算法
7.5图的遍历
从某一顶点出发,遍历图中其他顶点,其每个顶点只被访问一次。需要在遍历过程中,把访问过的顶点搭上标记,即设置一个数组vistied[n],初始为0,访问过后为1
7.5.1深度优先遍历DFS
深度优先是一个递归的过程,是一个树的前序遍历
邻接矩阵
typedef int Boolean;
Boolean visited[MAX];//访问标志的数组