图
图的定义和基本术语
1、图的定义
图:G(Graph) = (V,E)
V(Vertex/顶点):顶点(数据元素)的有穷非空集合;
E(Edge/边):边的有穷集合。
无向图:每条边都是无方向的
有向图:每条边都是有方向的
完全图:任意两个点都有一条边相连
–> 无向完全图:n个顶点, n(n-1)/2条边
有向完全图:n个顶点, n(n-1)条边
稀疏图:有很少边或弧的图(e < nlogn)。
稠密图:有较多边或弧的图。
网:边、弧带权的图。
邻接:有边、弧相连的两个顶点之间的关系。
无向// 存在(vi,vj),则称vi和vj互为邻接点;
有向//存在<vi,vj>,则称vi邻接到vj,vj邻接于vi
关联(依附):边/弧与顶点之间的关系。
存在(vi,vj)/<vi,vj>,则称该边/弧关联于vi和vj
顶点的度:与该顶点相关联的边的数目,记作TD(v)
有向图中—顶点的度等于该顶点的入度与出度之和。
顶点v的入度是以v为终点的有向边的条数,记作ID(v)
顶点v的出度是以v为始点的有向边的条数,记作OD(v)
如下图示:
顶点 | 度 |
---|---|
V0 | 2 |
V1 | 3 |
V2 | 3 |
V3 | 2 |
V4 | 2 |
顶点 | 入度 | 出度 | 度 |
---|---|---|---|
V0 | 1 | 2 | 3 |
V1 | 1 | 0 | 1 |
V2 | 1 | 1 | 2 |
V3 | 1 | 1 | 2 |
图的存储结构
1、邻接矩阵
邻接矩阵的存储表示:用两个数组分别存储顶点表和邻接矩阵
#define MaxInt 32767 //对网来说,用来表示∞(极大值)
#define MVNum 100 //最大顶点数
typedef char VerTexType; //顶点数据类型为字符型
typedef int ArcType; //假设边的权值类型为整型
typedef struct{
VerTexType vexs[MvNum]; //顶点表
ArcType arcs[MVNum][MVNum];//邻接矩阵
int vexnum,arcnum; //图的当前点数和边数
}AMGraph; //Adjacent Matrix Graph//邻接矩阵图
----采用邻接矩阵表示法创建无向网:
算法思想:
(1)输入总顶点数和总边数。
(2)依次输入点的信息存入顶点表中。
(3)初始化邻接矩阵,使每个权值初始化为极大值。
(4)构造邻接矩阵。
//在图中查找顶点
Status LocateVex(AMGraph G,VertexType v){
//图G中查找顶点v,存在则返回顶点表中的下标;否则返回-1
int i;
for(i = 0;i < G.vexnum;++i){
if(v == G.vexs[i]){//在定点表中找出v
return i;
}
}
return -1;
}
Status CreateUDN(AMGraph &G){
cin>>G.vexnum>>G.arcnum;//输入总顶点数,总边数
for(i=0;i<G.vexnum;++i){
cin>>G.vexs[i]; //依次输入顶点信息
}
for(i=0;i<G.vexnum;++i){
for(j=0;j<G.vexnum;++j){
G.arcs[i][j] = MaxInt; //初始化边的权值为极大值
}
}
for(k=0;k<G.arcnum;++k){//构造邻接矩阵
cin>>v1>>v2>>w; //输入一条边所依附的顶点及边的权值
i = LocateVex(G,v1);
j = LocateVex(G,v2); //找到v1,v2在G中的位置
G.arcs[i][j] = w; //边<v1,v2>的权值置为w
G.arcs[j][i] = G.arcs[i][j];//对称
}
return OK;
}
无向网
—>无向图
1、初始化邻接矩阵时,w均为0
2、构造邻接矩阵时,w为1
—>有向网
邻接矩阵是非对称矩阵
仅为G.arcs[i][j]赋值
无需为G.arcs[j][I]赋值。
有向图
–>
1、初始化邻接矩阵时,w均为0
2、构造邻接矩阵时,w为1
3、仅为G.arcs[i][j]赋值
无需为G.arcs[j][I]赋值。
邻接矩阵的优点
直观、简单、好理解
方便检查任意一对顶点间是否存在边
邻接矩阵的缺点
2、邻接表(链式)
邻接表存储表示
//边结点结构
#define MVNum 100 //最大顶点数
typedef struct ArcNode{ //边结点
int adjvex; //该边所指向的顶点的位置
struct ArcNode * nextarc;//指向下一条边的指针
OtherInfo; //和边相关的信息
}ArcNode;
//顶点结点结构
typedef struct VNode{
VerTexType data; //定点信息
ArcNode * firstarc; //指向第一条依附该顶点的边的指针
}VNode,AdjList[MVNum]; //AdjList表示邻接表类型
//图的结构定义
typedef struct{
AdjList vertices; //顶点的数组类型,vertices是vertex的复数
int vexnum,arcnum; //图的当前顶点数和弧数
}ALGraph;
采用邻接表表示法创建无向网
算法思想
(1)输入总顶点数和总边数
(2)建立顶点表
依次输入点的信息存入顶点表中
使每个表头结点的指针域初始化为NULL
(3)创建邻接表
依次输入每条边依附的两个顶点
确定两个顶点的序号i和j,建立边结点
将此边结点分别插入到vi和vj对应的两个边链表的头部
Status CreateUDG(ALGraph &G){
cin>>G.vexnum>>G.arcnum; //输入总顶点数
for(i=0;i<G.vexnum;++i){ //输入各点,构造表头结点表
cin>>G.vertices[i].data; //输入顶点值
G.vertices[i].firstarc = NULL; //初始化表头结点的指针域
}
for(k=0;k<G.arcnum;++k){ //输入各遍,构造邻接表
cin>>v1>>v2; //输入一条边依附的两个顶点
i = LocateVex(G,c1);
j = LocateVex(G,v2);
p1 = new ArcNode; //生成一个新的边结点*p1
p1->adjvex = j; //临界点序号为j
p1->nextarc = G.vertices[i]starc;
G.vertices[i].firstarc = p1; //将新结点*p1插入顶点vi的边表头部
p2 = new ArcNode; //生成另一个对称的新的边结点*p2
p2->adjvex = i; //邻接点序号为i
p2->nextarc = G.vertices[j].firstarc;
G.vertices[j].firstarc = p2; //将新结点*p2插入顶点vj的边表头部
}
return OK;
}
图的遍历
图的遍历特点:
图中可能存在回路,且图的任一顶点都有可能与其他顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点
故为了避免重复访问,我们设置一个辅助数组visited[n],用来标记每个被访问过的顶点。
- 初始状态为visited[i]为0
- 顶点i被访问,改visited[i]为1,防止被多次访问。
1、深度优先遍历(Depth_First Search – DFS)
1、采用邻接矩阵表示图的深度优先搜索遍历
void DFS(AMGraph G,int v){
cout<<v;
visited[v] = true; //访问第v个顶点
for(w=0;w<G.vexnum;w++){//依次查看邻接矩阵v的所在的行
if((G.arcs[v][w] != 0) && (!visited[w])){
DFS(G,w); //w是v的邻接点,如果w未访问,则递归调用DFS
}
}
}
2、广度优先遍历(Breadth_First Search – BFS)
1、按广度优先非递归遍历连通图G
void BFS(Graph G,int v){
cout<<v;
visit[v] = true; //辅助数组访问第v个顶点
InitQueue(Q); //辅助队列Q初始化,置空
EnQueue(Q,v); //v进队
while(!QueueEmpty(Q)){ //队列非空
DeQueue(Q,u); //队头元素出队并置为u
for(w = FirstAdjVex(G,u);w >= 0;w = NextAdjVex(G,u,w)){
if(!visited[w]){ //w为u的尚未访问的邻接顶点
cout<<w;
visited[w] = true;
EnQueue(Q,w); //进队
}
}
}
}