数据结构与算法
第一章 绪论
第二章 线性表
第三章 树与二叉树
第四章 图
第五章 查找
第六章 排序
文章目录
第四章 图
一、图的基本概念
1、图: G(V,E) V:顶点的集合 E:边的集合
2、无向图
(
v
i
,
v
j
)
(v_i,v_j)
(vi,vj)、有向图
<
v
i
,
v
j
>
<v_i ,v_j>
<vi,vj>
3、简单图
4、完全图:n个顶点的完全图有
n
∗
(
n
−
1
)
2
\frac{n*(n-1)}{2}
2n∗(n−1)条边
5、有向完全图:n个顶点的有向完全图有
n
∗
(
n
−
1
)
n*(n-1)
n∗(n−1)条边
6、邻接、依附
7、网:带权的图
8、度:握手定理,出度=入度
9、路径:路径长度为路径上的边数(权值之和)
10、环(圈)
11、连通图、连通分量(非连通图的极大连通子图)
12、强连通图、弱连通图
13、生成树
二、图的存储结构
1、邻接矩阵
顶点不分大小主次,用一维数据来存储
边是顶点和顶点之间的关系,用二维数组来存储->邻接矩阵
$
\begin{bmatrix}
0 & 1 & 1 & 1 \
1 & 0 & 1 & 0 \
1 & 1 & 0 & 1 \
1 & 0 & 1 & 0 \
\end{bmatrix}
$
2、邻接表
无向图
有向图的正邻接表与逆接表
//边表结点
typedef struct node{
int adjvex;
EdgeData cost;
struct node *next;
}EdgeNode;
//顶点表结点
typedef struct{
VertexData vertex ;
EdgeNode *firstedge;
}VertexNode;
//图的邻接表
typedef struct{
VertexNode vexlist[NumVertices];
int n,e;
}AdjGraph;
建立算法:
void CreateGraph (AdjGraph G){
cin >> G.n >> G.e;
for (int i =0;i<G.n;i++){
cin >> G.vexlist[i].vertex;
G.vexlist[i].firstedge = NULL;
}
for (int i = 0; i<G.e; i++){
cin >> tail >> head >> weight;
EdgeNode *p = new EdgeNode;
p->adjvex = head;
p->cost = weight;
p->next = G.vexlist[tail].firstedge;
G.vexlist[tail].firstedge = p;
p = new EdgeNode;
p->adjvex = tail;
p->cost = weight;
p->next = G.vexlist[tail].firstedge;
G.vexlist[tail].firstedge = p;
}
}
3、十字链表
可以看成是将有向图的正邻接表和逆邻接表结合起来得到的一种链式存储结构
4、邻接多重表
邻接多重表,是对无向图的邻接矩阵的一种压缩表示邻接多重表的结构与十字链表类似。在邻接多重表中,所有依附于同一顶点的边串联在同一链表中,由于每条边依附两个顶点,则每个边结点同时链接在两个链表中。
三、图的搜索(遍历)
1、DFS
bool visited[NumVertices];
int dfn[NumVertices];
void DFSTraverse(AdjGraph G){
int count = 1;
for(int i = 0;i< G.n;i++){
visited[i] = false;
}
for (int i=0;i<G.n;i++){
if(!visited[i]){
DFSX(G,i);
}
}
}
时间复杂度:
邻接矩阵
O
(
n
2
)
O(n^2)
O(n2),邻接表
O
(
n
+
e
)
O(n+e)
O(n+e)
空间复杂度:
邻接矩阵
O
(
n
)
O(n)
O(n),邻接表
O
(
n
)
O(n)
O(n)
2、BFS
bool visited[NumVertices];
int dfn[NumVertices];
void BFSTravverse(AdjGraph G){
int count = 1;
memset(visited,false,sizeof(visited));
for (int i =0;i<G.n;i++){
if(visited[i]){
BFSX(G,i);
}
}
}
void BFS1(AdjGraph *G,int k){
int i;
EdgeNode *p;
QUEUE Q;
MAKENULL(Q);
cout << G->vexlist[k].vertex;
visited[k] = true;
ENQUEUE(k,Q);
while(!Empty(Q)){
i = DEQUEUE(Q);
p = G->vexlist[i].firstedge;
while (p){
if(!visited[p->adjvex]){
cout << G->vexlist[p->adjvex].vextex;
visited[p->adjvex] = true;
ENQUEUE(p->advex,Q);
}
p = p->next;
}
}
}
时间复杂度:
邻接矩阵
O
(
n
2
)
O(n^2)
O(n2),邻接表
O
(
n
+
e
)
O(n+e)
O(n+e)
空间复杂度:
邻接矩阵
O
(
n
)
O(n)
O(n),邻接表
O
(
n
)
O(n)
O(n)
四、最小生成树算法
1、prim算法
void Prim(Costtype C[n+1][n+1]){
costtype LOWCOST[n+1];
int CLOSSET[n+1];
int i,j,k;
costtype min;
for(i = 2;i<=n;i++){
LOWCOST[i] = C[1][i];
CLOSSET[i] = 1;
}
for(int i =2;i<=n;i++){
min = LOWCOST[i];
k = i;
for(j = 2;j<=n;j++){
if(LOWCOST[j]<min){
min = LOWCOST[j];
k = j;
}
}
cout << "(" << k << "," << CLOSSET[k]<<")"<<endl;
LOWCOST[k] = infinity;
for(j = 2;j<=n;j++){
if(C[k][j]<LOWCOST[j] && LOWCOST[j]<infinity){
LOWCOST[j] = C[k][j];
CLOSSET[j] = k;
}
}
}
}
时间复杂度:
O
(
v
2
)
O(v^2)
O(v2)
Prim算法的时间复杂度不依赖于|E|,因此它适用于求解边稠密的图的最小生成树。
2、kruskal算法
void Kruskal(EdgeSet edges,int vexnum,int arcnum){
int bnf,edf;
int parents[100];
Sort(edges);
memset(parents,0,sizeof(parents));
for(int i = 0;i<arcnum;i++){
bnf = Find(edges[i].begin,parents);
edf = Find(edges[i].end,parents);
if(bnf!=edf){
parents[bnf]=edf;
cout << "(" << vertices[edges[i].begin].data << "," << vertices[edges[i].end].data<<","<< edges[i].cost<<")"<<endl;
}
}
}
时间复杂度:
O
(
E
∗
log
E
)
O(E*\log{E})
O(E∗logE)
Kruskal是一种按权值的递增次序选择合适的边来构造最小生成树的方法适用于边稀疏而顶点较多的图
五、双连通性算法(略)
六、强连通性算法(略)
七、最短路径算法(SPF)
1、Dijkstra算法
void Dijkstra(GRAPH C, costtype D[n+1],int P[n+1],bool S[n+1]){
for (int i = 1;i<=n;i++){
D[i] = C[1][i];
S[i] = false;
}
S[1] = true;
for(int i = 1;i<n;i++){
w = MinCost(D,S);
s[W] = true;
for(int v = 2;v<=n;n++){
if(S[v]!=true){
sum = D[w]+C[w][v];
if(sum<D[v]){
D[v] = sum;
P[v] = w;
}
}
}
}
}
costtype MinCost(D,S){
temp = infi;
w = 2;
for(int i =2;i<=n;i++){
if(!S[i] && D[i]<temp){
temp = D[i];
w = i;
}
}
return w;
}
2、Floyd算法
void Floyd(costtype D[][],costtype C[][],int P[][],int n){
for(int i=0;i<n;i++){
for (int j = 0;j<n;j++){
D[i][j] = C[i][j];
P[i][j] = -1;
}
}
for(int k = 0;k<n;k++){
for(int i =0;i<n;i++){
for(int j =0;j<n;j++){
if(D[i][k]+D[k][j]<D[i][j]);
D[i][j]=D[i][k]+D[k][j];
P[i][j] = k;
}
}
}
}
八、拓扑排序算法
AOV网(Activity On Vertex Network)
在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,称这样的有向图为顶点表示活动的网,简称AOV网
AOV网中不能出现回路
DAG:有向无环图
void Topolpgizalsort(AdjGraph G){
QUEUE Q;
nodes = 0;
NAKENULL(Q);
for(v = 1;v <=G.n ; v++){
if(indegree[v] = 0);
ENQUEUE(v,Q);
while (!EMPTY(Q)){
v = FRONT(Q);
DEQUEUE(Q);
cout << v;
nodes++;
}
for(邻接与v的每个顶点w){
if((--indegree[w])){
ENQUEUE(w,Q);
}
}
if(nodes < n){
cout << "图中有环路";
}
}
}
九、关键路径算法
AOE网(Activity On Edge Network)
事件
v
k
v_k
vk的最早发生时间ve(k)
ve(k) = MAX{ve(k)+weight(
v
j
,
v
k
v_j,v_k
vj,vk)}
按照从前往后的顺序进行,可以在拓扑排序的基础上计算
事件
v
k
v_k
vk的最晚发生时间vl(k)
vl(k) = Min{vl(j)-weight(
v
k
,
v
j
v_k,v_j
vk,vj)}
按照从后往前的顺序进行,可以在逆拓扑排序的基础上进行计算
活动
a
i
a_i
ai的最早开始时间e(i)
e(i) - ve(k)
活动
a
i
a_i
ai的最晚开始时间l(i)
l(i) = vl(j)-weight(
v
k
,
v
i
v_k,v_i
vk,vi)