图的分类:
有向图,无向图
连通图,非连通图
连通图分为强连通(有向并且形成一个环)和弱连通(有向并且连成一串但是不是一个环)
图的存储
用邻接矩阵存储有向图或者无向图
#include
using namespace std;
#define INFINITY 32767 //权值最大值
#define MVNUM 100//最多顶点个数
#define ERROR 0
typedef char VertexType;//顶点的类型
typedef enum{DG,DN,UDG,UDN}GraphKind;//图的类型
typedef char InfoType;
typedef int Status;
//弧的类型
typedef struct ArcType
{
int adj;//是否邻接(0/1)或者填权值(w/INFINITY)
InfoType *info;//这个弧的信息
}ArcType;
//定义邻接表
typedef struct
{
VertexType vexs[MVNUM];//便于后面加弧的时候加下标
ArcType arcs[MVNUM][MVNUM];
int vexnum,arcnum;//顶点数和弧数
GraphKind kind;
}MGraph;
//创建一个无向网
Status CreatUDN(MGraph &M)
{
cin>>M.arcnum>>M.vexnum;
for(int i=0;i>M.vexs[i];
//先初始化所有的弧的权值为无穷大。
for(int i=0;i>v1>>v2>>w;
int i=GetLocate(M,v1);
int j=GetLocate(M,v2);
M.arcs[i][j].adj=w;
M.arcs[j][i]=w;//因为是无向图
}
}
用邻接表存储图
#include
using namespace std;
#define OK 1
typedef enum{DG,,DN,UDG,UDN} GraphKind;
typedef int Status;
typedef int VertexType;
typedef char InfoType;
//这三个结构体看起来简单
//其实反映了先建弧,然后建起点,然后建表的思路
//当前结点指向的弧
typedef struct ArcNode
{
int adjvex;//存弧头(弧的终点)
struct ArcNode* nextarc;
InfoType *Info;
}ArcNode;
//结点
typedef struct VNode
{
VertexType data;
ArcNode * firstarc;
}VNode,*AdjList;
//邻接表图
typedef struct
{
VertexType *lis;//存表头
AdjList vertices;
int vexnum,arcnum;
GraphKind kind;
}AlGraph;
//创建一个邻接表
Status CreatAl(AlGraph &A)
{
cin>>A.arcnum>>A.vexnum;
vertices=(VNode*)mallloc(sizeof(VNode)*(A.vexnum+1));
for(int i=0;i>A.vertices[i].data;
A.vertices[i].firstarc=NULL;
}
for(k=0;k>v1>>v2>>w;
int i,j;
i=GetLocate(A,v1);
j=GetLocate(A,v2);
//用头插法
ArcNode* arc=(ArcNode*)malloc(sizeof(ArcNode));
arc->adjvex=j;
arc->nextarc=A.vertices[i].firstarc;
A.vertices[i].firstarc=arc;
}
return OK;
}
十字链表
//十字链表
//一个结点除了有行数列数和value值
//还有right(指向同一行的右边的一个结点)
//down(指向同一列的下一个结点)
//结点定义
typedef struct LNode
{
int row,col;
ElemType value;
struct LNode* right,*down;
}LNode,*Link;
//表的定义
typedef struct
{
Link *row_head,*col_head;//行表头,列表头
int m,n,num;//行数列数和非零数的个数
}CrossList;
图的遍历(深度优先遍历和广度优先遍历)
深度优先:
就是一直向前延伸,找与当前点连接的没有被访问的点,如果走投无路了,就返回
关注于每一步,每一步都找没有被访问过的连接的点
代码实现:
在连通图中当访问到V4的时候,没有点了就回溯到V3,
其实上一个步骤是在V3的第一个循环里面实现的,这个循环走到头了就进行下一个循环。
让每个顶点都访问一遍是因为有非连通图。
//一个顶点一条路走到黑的操作
//也就是连通图的操作
void DFS(Graph G, int v, Status (*visit)(ElemType))
{
visit (v);
visited[v]=TRUE; //visited为全局数组
for(w=FirstAdjVex(G,v); w>=0; w=NextAdjVex(G,v,w) )
if(!visited[w])
DFS(G, w, visit);
return;//意思是回溯,如果这个点已经被访问到了,就回溯到上一个点。
}
//每个顶点调用一次
//因为可能是非连通图
void DFSTraverse(Graph G,Status(*visit)(int v))
{
for(v=0;v
广度优先:
每次都访问与当前点连接的所有的点,一点一点地扩大
用到队列
void BFS(Graph G,int v,Status(*visit)(ElemType))
{
visit(v);//先访问第一个结点
InitQueue(Q);
EnQueue(Q,v);//入队
while(!Q.empty())//只要不空
{
DeQueue(Q,v);//先出队
//让所有的点入队
for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w))
{
if(!visited[w])
{
visit(w);
visited[w]=true;
EnQueue(Q,w);
}
}
}
}
//对于不连通的情况
void BFSTraverse(Graph G,Status(*visit)(int v))
{
for(v=0;v
求最小生成树的Prim算法(贪心的思想)
整体的思路是:把整个图的结点分成两个集合 U V
U:存最小生成树的所有节点(其实可以直接拿一个二维数组来存)
用book数组来记录结点是否进入了最小生成树.
用dis[]来存所有节点到最小生成树 的最小距离
从把第一个结点放入最小生成树,就更新dis[],只要到当前结点的距离小了,就更新
**其实为了得到一个完整的邻接图,dis 可以是抽象数据类型,每个元素可以存权值以及弧的起点终点。
每次都找最小的那个结点加入,继续循环。
直到所有的结点都book掉了。
Kruskal算法(贪心的策略)
所有的点都是一个连通分支,每一次都把顶点不在一个连通分支里面的最小的边加入到最小生成树里面中,直到所有边都看过或者边数成为点数-1。
对于起初所有节点都是一个连通集,可以想到用并查集的方式,最初让所有节点的father都是自己,在遍历边的过程中,只要两个点的根节点不一样,可以把边加入最小生成树,同时让其中一个点的father为另一个点。这样就在一个集合里面了。
拓扑排序:
选入度为0的顶点输出(若有多个则全部入栈后从栈顶开始输出)并删除, 至最后看图空,否则有回路
关键路径算法:
初始化ve为0,找入度0的顶点,更新其后继ve, 删除并入栈,至图空或有回路;初始化vl为工期,按拓扑逆序求各顶点vl.最后ee(a,b)=ve(a),el(a,b)=vl(b)-w(a,b)
Floyd总结:优美的代码需要更深度的思维
创建二维的string数组的时候第一次要用malloc ,第二次要用new
//在求任意两点之间的距离的时候可以用
/*弗洛伊德算法*/
#include
using namespace std;
#define Infi 100000
//太优美了,我明白了
//一直以为是次都是经过一个点,
//其实你在试第二个点的时候,你以为是A->第二个点->B
//其实A->第二个点中间是有之前循环中点参与的最小值,
//所以是A->……->第二个点->……B
//所以大胆地循环吧
void Floyd(int **a,int num,string **mapp)
{
for(int i=0;i>num>>lu;
S=(string**)malloc(sizeof(string*)*(num+1));//这一层不用new
arc=(int**)malloc(sizeof(int*)*(num));
for(int i=0;i>qi>>zh>>dis;
arc[qi][zh]=dis;
string qz=(to_string(qi)+to_string(zh));//把起点终点变成string添加
cout<<"qz为"<
Dijkstra算法:贪心的思想
/*Dijkstra算法*/
//往往用于求固定两个点之间的最短路
#include
using namespace std;
#define Inf 1000000
//每次找出没有确定最短路的点中的最短的,然后扩展它周围的点
//1.要记录没有确定最短路的点
//2.要记录并初始化每个点的最短路长
//3.记录每个点的最短的路径的具体路径
//循环结束的条件是所有点都确定了最短路
int Find_Pos(int sure[],int dis[],int num)//找到还没被确定的最短路所在的位置
{
int minn=Inf,minpos=-1;
for(int i=0; i>num>>numofroad>>qi>>zh;
int *sure=(int*)malloc(sizeof(int)*num);
int *dis=(int*)malloc(sizeof(int)*num);
int **M=(int**)malloc(sizeof(int*)*num);
string *mapp=new string[num];
for(int i=0; i>q>>z>>dis;
M[q][z]=dis;
}
// for(int i=0;i"<
1.若无向图G =(V,E)中含10个顶点,要保证图G在任何情况下都是连通的,则需要的边数最少是:9*8/2=36 36+1=37
2.
给定一个有向图的邻接表如下图,则该图有__个强连通分量。
3 {{2}, {4}, {0, 1, 3, 5}}
强连通分量
3.如果G是一个有36条边的非连通无向图,那么该图顶点个数最少为多少?
如果是一个连通无向图,36*2=72 72=8*9,9个点,9+1=10
4.设N个顶点E条边的图用邻接表存储,则求每个顶点入度的时间复杂度为
O(N+E)
5.在N个顶点的无向图中,所有顶点的度之和不会超过顶点数的多少倍?
度之和是N(N-1),
6.具有N(N>0)个顶点的无向图至多有多少个连通分量?
最少有1个,最多N个
7. 一个有N个顶点的强连通图至少有多少条边?
强连通是有向图,最多有N(N-1) 最少有N 就是一个环
8.对于有向图,其邻接矩阵表示比邻接表表示更易于:求入度
9.已知无向图G含有16条边,其中度为4的顶点个数为3,度为3的顶点个数为4,其他顶点的度均小于3。图G所含的顶点个数至少是:
11
10.在AOE图中,关键路径上某个活动的时间缩短,整个工程的时间也就必定缩短。F
因为有许多关键路径,如果缩短所有关键路径的公共点的时间整个工期就会缩短
11.在AOE图中,关键路径上活动的时间延长多少,整个工程的时间也就随之延长多少。T
12.AOE图的关键路径就是最长的路径T
13.如图所示的AOE-网
,事件④的最迟发生时间是。37
最早发生时间是从起点开始的最长路,最晚是从下一个结点往前找的最短路
14.
如图所示的AOE-网,求这个工程最早可能在什么时间结束。18
最早结束的时间就是就是最长的。
15.Kruskal 算法是通过每步添加一条边及其相连的顶点到一棵树,从而逐步生成最小生成树。F
并不是,每次都是连接最小的且两个点不在一个连通分量里面
16.Prim 算法是通过每步添加一条边及其相连的顶点到一棵树,从而逐步生成最小生成树。T
17.对于带权无向图 G = (V, E),M 是 G 的最小生成树,则 M 中任意两点 V1 到 V2 的路径一定是它们之间的最短路径。F
18.如果 e 是有权无向图 G 唯一的一条最短边,那么边 e 一定会在该图的最小生成树上。F
19.若图G为连通图且不存在拓扑排序序列,则图G必有环。F
20.最小生成树用kuskal算法,最小生成树不唯一
21.在拓扑排序算法中用堆栈和用队列产生的结果会不同吗?可能相同可能不同
22.
下图为一个AOV网,其可能的拓扑有序序列为:ABDCEFG
//依次找入度为零的点