图的相关概念
1、图是元素之间存在多对多的关系(线性表的元素之间存在前驱和后继,树的元素之间存在父子关系,图的任意元素之间都有可能存在关系)
2、图是由顶点的有穷非空集合和顶点之间边的集合组成。
3、在图型数据结构中,数据被称为顶点,数据之间的关系称为边。
4、在图中不允许出现没有点,但可以没有边。
5、G(V,E) V表示顶点的集合,E表示边的集合。
各种图的定义
1、无向图:顶点与顶点之间没有方向,称为无向边,边用无向序偶对表示(v,v1)。
如:V={A,B,C,D} E={(A,B),(B,C),(C,D),(D,A),(A,C)}。
2、在无向图中,如果任意两个顶点之间都存在边,称为无向完全图。
3、有向图:若顶点之间有方向,这种边称为有向边,也叫弧,边用有序偶对表示<v,v1>,箭头的方向v1是弧头,v是弧尾。
注意:若不存在顶点到自身的边,也不存在重复出现的边,这种图叫简单图,数据据结构课程中讨论的都是简单图。
4、在有向图中,如果任意两个顶点之间都存在方向相反的两条弧,这种图叫有向完全图。
5、图中有很少边或弧的图叫稀疏图,反之叫稠密图。
6、如果图中的边或弧有相关的数据,数据称为权,这种图叫网(带权图)。
7、如果G(v,E)和G1(v1,E1),存在V>=v1,E>=E1,那么G1是G的子图。
顶点和边的关系
1、顶点的度:指的是顶点相关联的边或弧的条目数,有向图又分为入度和出度。
2、入度指的是其他顶点到该顶点的弧的条目数。
3、出度值得是从该顶点到其他顶点的弧的条目数。
4、顶点序列:从一个顶点到另一个顶点的路径,路径长度指的是路径上的边或弧的条目数。
连通图的相关术语
1、在无向图中,在顶点v到v1之间有路径,则称v到v1之间是连通的,如果任意两个顶点都是连通的,那么这种图称为连通图。
2、无向图中的极大连通子图称为连通分量。
1、必须是子图
2、子图必须是连通的
3、连通子图含有极大的顶点数
3、在有向图中,任意顶点之间都存在路径,这种图叫强连通图。
4、有向图中的极大连通子图称为有向的强连通分量。
5、在有向图中如果有一个顶点的入度为0,其他顶点的入度均为1,则是一棵有向树。
图的存储结构
图的存储主要存储两个方面:顶点和边。
邻接矩阵
1、邻接矩阵:一个一维数组(顶点)和一个二维数组(边、弧)组成。
2、二维数组i,i位置都是0,如果都是无向图则数组对称(左上到右下的对角线为轴)。
3、优点:
- 非常容易判定两点之间是否有边
- 非常容易计算两个点之间入度和出度
- 非常容易统计邻接点
缺点:如果存储稀疏图,会非常浪费存储空间
邻接表
1、邻接表由顶点表和边表组成。
顶点 下一个阾接点地址 项点下标|下一个阾接点地址
A -> [1] -> [3]-> NULL []
B -> [2]->NULL
C -> [4]-> [3]-> NULL
D
E
2、优点:
- 节省存储空间
- 非常容易计算出度
缺点:不方便计算入度
十字链表
由于邻接表不能同时兼顾出度和入度,因此我们修改邻接表的边表结构,使用既存储入度也存储出度,这种表就叫十字链表。
邻接多重表
由于遍历表是需要一些删除边操作,而阾接表在删除边时非常麻烦,因此就设计出的阾接多重表。
边集数组
由两个一维数组构成,一个存储顶点的信息,另一个存储边的信息(它的每个数据元素都由一条边的起点到终点的下标和权组成),这种存储结构更侧重于边的相关操作(路径、路径长度、最短路径),而统计顶的度需要扫描整个数组,效率不高。
图的遍历
注意:图的遍历结果无论是深度优点还是广度优先都不是唯一的。
1、深度优先:类似树的前序遍历
void _dfs_ergodic(Graph* gra,int i,bool* flag)
{
if(flag[i]) return;
printf("%c ",gra->verter[i]);
flag[i] = true;
for(int j=0; j<gra->size; j++)
{
if(gra->edge[i][j])
_dfs_ergodic(gra,j,flag);
}
}
// 深度遍历
void dfs_ergodic(Graph* gra)
{
bool flag[gra->size];
for(int i=0; i<gra->size; i++)
flag[i] = false;
for(int i=0; i<gra->size; i++)
{
_dfs_ergodic(gra,i,flag);
}
printf("\n");
}
2、广度优先:类似树的层序遍历,与树一样也需要使用队列配合。
void _bfs_ergodic(Graph* gra,int i,bool* flag,Queue* queue)
{
if(flag[i]) return;
push_queue(queue,i);
for(int j=0; j<gra->size; j++)
{
if(gra->edge[i][j])
push_queue(queue,j);
while(!empty_queue(queue))
{
int k = *head_queue(queue);
if(!flag[k])
{
printf("%c ",gra->verter[k]);
flag[k] = true;
}
for(int l=0; l<gra->size; l++)
{
if(gra->edge[k][l])
push_queue(queue,l);
}
pop_queue(queue);
}
}
}
// 广度遍历
void bfs_ergodic(Graph* gra)
{
Queue* queue = creat_queue();
bool flag[gra->size];
for(int i=0; i<gra->size; i++)
flag[i] = false;
for(int i=0; i<gra->size; i++)
{
_bfs_ergodic(gra,i,flag,queue);
}
destory_queue(queue);
printf("\n");
}