思维导图
笔记
度
分为出度和入度,出度为顶点指向其他顶点的边,入度为指向该顶点的边.
连通图
概念
连通: 在无向图G中,如果从顶点v1到顶点v2有路径,则称v1和v2是连通的
连通图:如果对于无向图中任意两个顶点vi,vj,vi和vj都是连通的,则称该图是连通图.
连通分量:无向图中的极大连通子图称为连通分量.
强连通图:在有向图中,任如果意两个顶点vi,vj,vi和vj都是连通的,则称该图是强连通图.
强连通分量:有向图中的极大强连通子图称作有向图的强连通分量.
无向连通图
一个无向连通图:任何两个节点之前都是连通的,都存在一条路径,并且图中没有方向.
1. 顶点的度为顶点所连接的边的个数,无向连通图中的顶点的度之和 为边数*2,所以顶点的度之和为偶数;
2. 边数可以等于顶点个数;
3. 如果为一条边两个节点的话 ,存在了两个顶点的度都为1.
有向连通图
从任何一个顶点出发都能遍历所有的顶点.
图的遍历
BFS
void BFSTraverse(AdjacencyMatrix* AM)
{
queue<int> Q;
for (int i = 0; i<AM->numVertexs; i++)
visited[i] = 0;
//InitQueue(&Q); //初始化一个空列表
for (int i = 0; i<AM->numVertexs; i++)
{
if (!visited[i])
{
visited[i] = 1;
cout << AM->vexs[i];
//EnQueue(&Q, i); //将i插入到队列的队尾
Q.push(i);
while (!Q.empty())
{
i = Q.front();
Q.pop();
for (int j = 0; j<AM->numVertexs; j++)
{
if (!visited[j] && AM->arc[i][j] != INF)
{
visited[j] = 1;
cout << AM->vexs[j];
//EnQueue(&Q, &j);
Q.push(j); //这就相当于把B和F分步输入到队列中
}
}
}
}
}
}
DFS
int visited[MAX]={0}
void DFS(AdjGraph * g ,int v)
{ ArcNode *p;
visited[v]=1;
printf(" %d ",v);
p=G->adjlist[v].firstarc;
while(p!=NULL)
{ if((visited[p->adjvex]==0)
DFS(G,p ->adjvex);
p=p->nextarc;
}
}
图的储存方式
邻接表
函数声明
typedef struct ANode
{
in adjvex;
struct ANode *nextrarc;
int weight;
}ArcNode;
typedef struct Vnode
{
InfoType info;
ArcNode *firstarc;
}VNode;
typedef struct
{
VNode adjlist[MAXV];
int n,e;
}AdjGraph
邻接矩阵
二维数组表示,数组的两个下标表示点到点,数组的值表示是否连接.
函数声明
#define maxvex 100
typedef struct
{
char vexs[maxvex];
int arc[maxvex][maxvex];
int vertex,edges;
}MGraph;
最小生成树
普里姆算法
普里姆算法是一种构造性算法,从候选边中挑选权值最小的边加入构造树,重复步骤,要注意构造树时,不能有环产生.
struct{
VertexType Adjvex;//顶点v到子集U中权最小边关联的顶点u
VRType lowcost;//顶点v到子集U中权最小边的权值
}closedge[maxn];
void MiniSpanTree_P(MGraph G, VertexType u)//从u出发构造G的最小生成树
{
k = LocateVex(G, u);
for(j = 0; j < G.vexnum; ++j){//辅助数组初始化
if(j != k)
closedge[j] = {u, G.arcs[k][j]};//各点到u的距离
}
for(i = 0; i < G.vexnum; i++){
k = minimum(closedge);//选择距离最小且不为0的作为k
printf(closedge[k].Adjvex, G.vexs[k]);//输出k对应的顶点和新加入的边权值
closedge[k].lowcost = 0;//加入新的点
for(j = 0; j < G.vexnum; j++){
if(G.arcs[k][j] < closedge[j].lowcost)//更新V-U中点到U中点的距离
closedge[j] = {G.vexs[k], G.arcs[k][j]};
}
}
}
克鲁斯卡尔算法
克鲁斯卡尔算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法.
算法说明
int find(int n)//找到点的树根
{
while(n != find[n])
n = find[n];
return n;
}
void join(int n, int m)//将两棵树并起来
{
int fn = find(n);//一定要找到两棵树的树根再并到一起
int fm = find(m);
find[fn] = fm;
}
int kruskal(int n, int m)
{
int num = 0;
for(i = 1; i <= n; i++)//初始化寻根函数
find[i] = i;
for(i = 0; i < m; i++){//此时A中边已经全部从小到大排好顺序
if(find(A[i].u) != find(A[i].v)){//如果边的顶点不属于一棵树
join(u, v);//并到一棵树
cost += A[i].w;
num++;//通过最后num是否达到n-1来判断是否有最小生成树
}
}
}
最短路径
Dijkstra
void SPath_Dij(MGraph G, int u0, int dis[], int Path[])
{
for(i = 0; i < G.vexnum; i++){
visited[i] = flase;
dis[i] = G.arcs[u0][i];
if(dis[i] < INFINITY) Path[i] = u0;//path用来记住前一个结点
else Path[i] = -1;
}
dis[u0] = 0; visited[u0] = true;
for(i = 0; i < G.vexnum; i++){
k = selectmin(dis);
visited[k] = true;//注意k此处应该被标记
for(j = 0; j < G.vexnum; j++){
if(dis[j] > dis[k] + G.arcs[k][j] && !visited[j]){
//选出的j不应该被访问过
dis[j] = dis[k] + G.arcs[k][j];
Path[j] = k;//注意对Path进行更新
}
}
}
}