图的定义和术语

图的定义

图G由两个集合构成,记作G=<V,E> ,其中V是顶点的非空有限集合,E是边的有限集合,其中边是顶点的无序对或有序对集合。

图的术语

无向图:若图G中所有边是无向边,则G为无向图

有向图:若所有边是有向边,则G为有向图;有向边被称作弧。

无向完全图:无向图且边数为n(n-1)/2

有向完全图:有向图且边数为n(n-1)

邻接点:边的两个顶点

关联边:若边e=(v,u),则称顶点v,u关联边e

顶点的度:一个顶点的度是与它关联的边的条数

入度:以该顶点为终点的有向边的条数,ID(v)

出度:以该顶点为起点的有向边的条数,OD(v)

顶点数、边数e、和度数D(v)的关系:e=$\sum_{i=1}^nD(V_i)$/2

无向图的路径、回路:

无向图G=(V,E)中的顶点序列$v_1$,$v_2$,…,$v_k$,若($v_i$,$v_{i+1}$) $\in$ E(i=1,2,…,k-1),$v=v_1$,$u=v_k$,则称该序列是从v到u的路径;若v=u,则该序列称为回路。

有向图的路径、回路:

有向图G=(V,E)中的顶点序列$v_1$,$v_2$,…,$v_k$,若<$v_i$,$v_{i+1}$> $\in$ E(i=1,2,…,k-1),$v=v_1$,$u=v_k$,则称该序列是从v到u的路径;若v=u,则该序列称为回路。

简单路径:序列顶点中不重复出现的路径

简单回路/环:在一条路径中,除起点和终点外所有顶点各不相同

连通图:在无向图G=中,若对于任何两个顶点v,u都存在v到u的路径,则称G是连通图。

强连通图:在有向图G=中,若对于任何两个顶点v,u都存在v到u的路径,则称G是连通图。

子图:设有两个图G=(V,E)、$G_1=(V_1,E_1)$,若$V_1$ $\subseteq$V,$E_1$ $\subseteq$E,$E_1$关联的顶点都在$V_1$中,称$G_1$是G的子图。

连通分量:无向图中的极大连通子图

强连通分量:有向图中的极大强连通子图

网络(边带权图):某些图的边具有与它相关的数,称为权,这种带权图叫网络

生成树:连通图的一个子图如果是一棵包含G所有顶点的树,则称为图G的生成树

图分类

图的抽象数据类型定义

ADT Graph{
数据对象V:V是具有相同特性的元素的集合称为顶点集。
数据关系: R={VR}
VR={<v,w>|v,w属于V且P(v,w),<v,w>表示从v到w的弧}
基本操作:
CreateGraph(&G) //建立图
DestoryGraph(&G) //销毁图
InsertVex(&G,v) //插入顶点
DeleteVex(&G,v) //删除顶点
DFSTraverse(G,v,visit()) //深度优先搜索
BFSTraverse(G,v,visit()) //广度优先搜索
}

图的存储结构

数组表示法(邻接矩阵)

G的邻接矩阵满足以下条件:

​ 邻接矩阵中的元素A[i] [j]存放的是顶点i到顶点j的关系的信息。

对于无向图

无向图的邻接矩阵特点:1.对称矩阵 2.对角线元素全0

无向图的第i个顶点的度怎么表示? 第i行(或第i列)的非零元素个数之和

图的总度数:矩阵中非零元素个数之和

边数为总度数的一半。

对于有向图

第i个顶点的出度和入度如何体现?

第i行的非零元个数之和为出度

第i列的非零元个数之和为入度

边数为非零元的个数

网络的邻接矩阵

对于无穷大,可以用INT_MAX表示(用一个特殊的数表示即可)

存储结构:

#include <iostream>
#include<string>
#define MAX_VERTEX_NUM 100
#define IFINITY INT_MAX
#define TRUE 1
#define FALSE 0

using namespace std;
typedef enum {DG,DN,UDG,UDN}GraphKind;
typedef struct VertexType{
char id;
// string label;
}VRType;

typedef struct InfoType {
int weight; // 用于存储边的权重
} InfoType;
typedef struct ArcCell{
int adj;
InfoType *info;
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct {
VertexType vexs[MAX_VERTEX_NUM];//顶点信息
AdjMatrix arcs;//邻接矩阵
int vexnum,arcnum;//顶点数,边数
GraphKind kind;//图种

}MGraph;

创建图:

void Creategraph(MGraph &G){
int i,j,k;float w;
cout<<"请输入顶点数,弧数,图类型:";
scanf("%d,%d,%d",&G.vexnum,&G.arcnum,&G.kind);
//构造顶点向量
for(i=0;i<G.vexnum;++i){
cout<<"输入顶点名称:";
cin>>G.vexs[i].id;
// scanf("%d",&G.vexs[i].id);
}
//初始化邻接矩阵
for(i=0;i<G.vexnum;++i){
for(j=0;j<G.vexnum;j++){
G.arcs[i][j].adj=IFINITY;
G.arcs[i][j].info=NULL;
}
}
//构造邻接矩阵
for(k=0;k<G.arcnum;k++){
cout<<"输入邻接矩阵位置与权重:";
scanf("%d,%d,%f",&i,&j,&w);
G.arcs[i][j].adj=w;
G.arcs[j][i].adj=w;
}
}
特点

1.空间复杂度O($n^2$)

2.边或弧的插入和删除操作简单,但顶点的插入和删除操作不容易

邻接表

对于每个顶点,建立一个单链表,存储该顶点的所有邻接顶点和信息·。

对于无向图

第i个顶点的总度数为边链表的结点的个数

图的总度数为所有边链表的结点个数之和。

边数即所有边链表的结点个数之和的一半

邻接表的定义

邻接表分为两部分,边链表和顶点表

存储结构:

#include <iostream>
#include<string>
#define MAX_VERTEX_NUM 20
using namespace std;
typedef enum {DG,DN,UDG,UDN}GraphKind;
typedef struct InfoType {
int weight;
}InfoType;
typedef struct vertextype{
char id;
// string label;
}vertextype;
//边表的存储结构
typedef struct ArcNode{
int adjvex; //该弧指向的顶点位置
struct ArcNode *nextarc;
InfoType *info;
}ArcNode;
//顶点表的存储结构
typedef struct vnode{
vertextype data;
ArcNode *firstarc;
}vnode,adjlist[MAX_VERTEX_NUM];
//邻接表的存储结构
typedef struct {
adjlist vertices;
int vexnum,arcnum;
GraphKind kind;
}ALGraph;

创建图:

void Creatadilist(ALGraph &G){
int i,j,k;
ArcNode *s;
scanf("%d,%d,%d",&G.vexnum,&G.arcnum,&G.kind);
for(i=0;i<G.vexnum;i++){
cin>>G.vertices[i].data.id;
G.vertices[i].firstarc=NULL;
}
for(k=0;k<G.arcnum;k++){
cin>>i>>j;
s=(ArcNode*)malloc(sizeof(ArcNode));
s->adjvex=j;
s->info=NULL;
s->nextarc=G.vertices[i].firstarc;
G.vertices[i].firstarc=s;
}
}
特点

1.空间复杂度O(n+e)

2.容易找到任意一个顶点的第一个邻接点

图的遍历

图的遍历指,从图的某一顶点出发,访问图中所有顶点,且每个顶点仅访问一次

深度优先搜索

基本思想

1.首先访问图中某一个顶点$V_1$,以该点为出发点

2.任选一个与顶点$V_i$邻接的未被访问的顶点$V_j$,访问$V_j$

3.以$V_j$为新的出发点继续进行深度优先搜索

算法概要

输入:图G,初始访问顶点v;

输出:图G的深度优先搜索序列;

1.访问v

2.改变v的访问标志

3.任选一个与v相邻又没被访问的顶点w

4.从w开始继续进行深度优先搜索

算法实现:

这里用邻接矩阵实现算法,传入的visit[]数组全部置0

void DFS(MGraph G, int v, bool visited[]) {
visited[v] = TRUE;
cout << G.vexs[v].id;
for (int w = 0; w < G.vexnum; w++) {
if (G.arcs[v][w].adj != -1 && !visited[w]) {
DFS(G, w, visited);
}
}
}

广度优先搜索

基本思想

1.首先访问图中某一个指定的出发点$v_i$,

2.然后一次访问$v_i$的所有邻接点$v_{i1}$,$v_{i2}$,$v_{i3}$…

3.再依次以$v_{i1}$,$v_{i2}$,$v_{i3}$…为顶点访问各顶点未被访问的邻接点,依次类推,直到图中所有顶点均被访问

算法概要

输入:图G,初始访问结点v

输出:图G的广度优先搜索序列

1.设置辅助队列Q,访问节点v,修改v的标志,v入队列

2.while(队列非空){

​ 出队列节点u

​ 访问与u的所有节点

​ 修改与u邻接的所有结点的标志,与u邻接的所有结点入队列

}

typedef struct QNode{
int data;
struct QNode *next;
}QNode,*QueuePtr;

typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;

void InitQueue(LinkQueue &Q)
{
Q.front=Q.rear=(QNode*)malloc(sizeof(QNode));
if(!Q.front)
exit(0);
Q.front->next=NULL;
}

void EnQueue(LinkQueue &Q,int e)
{
QueuePtr p=(QNode*)malloc(sizeof(QNode));
if(!p)
exit(0);
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
}

void DeQueue(LinkQueue &Q,int &e)
{
if(Q.rear==Q.front)
return;
else
{
QueuePtr p;
p=Q.front->next;
e=p->data;
Q.front->next=Q.front->next->next;
if(Q.rear==p)
Q.rear=Q.front;
free(p);
}
}
int QueueEmpty(LinkQueue Q){
if(Q.front==Q.rear) return 1;
else return 0;
}
LinkQueue Q;
void BFSTraVErse(ALGraph G,bool visit[]){
int v;
for(v=0;v<G.vexnum;++v){
visit[v]=FALSE;
}
InitQueue(Q);
for(v=0;v<G.vexnum;++v){
if(!visit[v]){
visit[v]=TRUE;
cout<<G.vertices[v].data.id;
EnQueue(Q, v);
while(!QueueEmpty(Q)){
int u,w;
ArcNode *p;
DeQueue(Q, u);
for(p=G.vertices[u].firstarc;p!=NULL;p=p->nextarc)//将取出的u结点遍历
{
if(!visit[p->adjvex])
{
w=p->adjvex;
visit[w]=TRUE;
EnQueue(Q,w);
cout<<G.vertices[w].data.id;
}
}
}
}
}
}

图的连通性问题

最小生成树

prim算法

//该图输入为字符型顶点,权值为浮点数且不为0
//部分注释代码为调试函数
#include <iostream>
#define MAX_VERTEX_NUM 100
using namespace std;
typedef struct ArcCell{
float adj; //记录权值
}ArcCell,G[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
struct Edge{
char begin;
char end;
float weight;
};
typedef struct{
G arcs;
int vexnum,arcsnum;
char vex[MAX_VERTEX_NUM];
Edge edge[MAX_VERTEX_NUM];
}MGraph;
struct V{
int adjvex;//closedeg[i].adjvex记录的是,顶点i到U中最短边的点
float lowcost;//lowcost记录权值
};
V closedge[MAX_VERTEX_NUM];
int Locatevex(MGraph G,char u){
int i;
for(i=0;i<G.vexnum;i++){
if(G.vex[i]==u){
return i;
}
}
return -1;
}
void CreateGraph(MGraph &G){

int i,j,k;
char name;
float w;
char ii, jj;
scanf("%d %d",&G.vexnum,&G.arcsnum);//输入顶点数和弧数

for(i=0;i<G.vexnum;i++){
getchar();
// cout<<"input name:";
scanf("%c",&name); //输入顶点名称,按次存储在vex[]数组中,下标从0开始
G.vex[i]=name;
}
for(i=0;i<G.vexnum;i++){
for(j=0;j<G.vexnum;j++){
G.arcs[i][j].adj=100;
}
}
for(i=0;i<G.arcsnum;i++){
getchar();
scanf("%c %c %f",&ii,&jj,&w);
j=Locatevex(G, ii);
k=Locatevex(G, jj);
G.arcs[j][k].adj=w;
G.arcs[k][j].adj=w;
}
}
void showMatrix(MGraph G){
int i,j;
for(i=0;i<G.vexnum;i++){
for(j=0;j<G.vexnum;j++){
cout<<G.arcs[i][j].adj<<" ";
}
cout<<endl;
}
}

void MiniSpanTree_P(MGraph G,char u){
// getchar();
V closedge[G.vexnum];
int t=Locatevex(G,u);
int i,j,k=0;
for(j=0;j<G.vexnum;j++){
if(j!=t){
closedge[j]={t,G.arcs[t][j].adj};
}
closedge[t].lowcost=0;
}

for(i=0;i<G.vexnum;i++){
/*for(int c=0;c<G.vexnum;c++){
cout<<closedge[c].adjvex<<" ";
}
cout<<endl;
for(int c=0;c<G.vexnum;c++){
cout<<closedge[c].lowcost<<" ";
}
cout<<endl;*/
float temp=100;
for(j=0;j<G.vexnum;j++){ //求k closedge最小值
if(closedge[j].lowcost==0){
continue;
}
else{
if(closedge[j].lowcost<temp){
k=j;
temp=closedge[j].lowcost;
}
}
}
// cout<<k<<endl;
printf("%c",G.vex[closedge[k].adjvex]);
if(i==G.vexnum-1){
cout<<endl;
break;
}
else{
printf("->");
}
closedge[k].lowcost=0;
t=k;
for(j=0;j<G.vexnum;j++){
if(!closedge[j].lowcost){
closedge[j].adjvex=t;
}
else{
closedge[j].adjvex=t;
closedge[j].lowcost=G.arcs[t][j].adj;
}
}

}
}

int main(){
MGraph G;
CreateGraph(G);
showMatrix(G);
cout<<"==============="<<endl;
char index;
cin>>index;
MiniSpanTree_P(G, index);
}

kruskal算法

#include <iostream>
#include <algorithm>

using namespace std;

struct Edge {
int src, dest;
float weight;
};

// 寻找父节点
int findParent(int parent[], int i) {
if (parent[i] == -1)
return i;
return findParent(parent, parent[i]);
}

// 合并两个子树
void unionSets(int parent[], int x, int y) {
int xset = findParent(parent, x);
int yset = findParent(parent, y);
parent[xset] = yset;
}

void kruskalMST(int V, Edge edges[], int E) {
Edge result[V - 1];
int i = 0; // 用于结果数组的索引
int e = 0; // 用于排序边的索引

// 对边按权重进行排序
sort(edges, edges + E, [](Edge a, Edge b) {
return a.weight < b.weight;
});

int parent[V]; // 用于检测环路
fill(parent, parent + V, -1);

while (i < V - 1 && e < E) {
Edge nextEdge = edges[e++];

int x = findParent(parent, nextEdge.src);
int y = findParent(parent, nextEdge.dest);

if (x != y) {
result[i++] = nextEdge;
unionSets(parent, x, y);
}
}

// 输出最小生成树的边和权重
cout << "Edges in the Minimum Spanning Tree:" << endl;
for (i = 0; i < V - 1; ++i) {
cout << result[i].src << " - " << result[i].dest << " : " << result[i].weight << endl;
}
}

int main() {
int V = 4; // 顶点数
int E = 5; // 边数
Edge edges[] = {
{0, 1, 10},
{0, 2, 6},
{0, 3, 5},
{1, 3, 15},
{2, 3, 4}
};

// 执行Kruskal算法并输出结果
kruskalMST(V, edges, E);

return 0;
}

有向无环图的应用

有向无环图简称DAG图

DAG图在工程计划和管理方面应用广泛。

拓扑排序

基本定义和特点

顶点活动网:AOV网,将顶点表示活动,边表示活动之间的关系

拓扑序列:把AOV网中的所有顶点排成可以线性序列,该序列满足:若AOV网中存在从$v_i$到$v_j$的路径,则在该序列中,$v_i$必位于$v_j$之前

拓扑排序:构造AOV网的拓扑序列的操作被称为拓扑排序

特点:

1.一个有向图的拓扑序列不一定唯一

2.有向无环图一定存在拓扑序列

3.有向有环图不存在拓扑序列

4.可以通过构造拓扑序列,判断AOV网是否存在环

拓扑排序算法的基本思想

1.在有向图中选一个入度为0的顶点输出

2。从图中删除该顶点及所有它的出边

3.重复 1和2 直到所有顶点输出或者图中剩余顶点入度均不为0(此时图中有回路,无法拓扑排序)

算法概要

增加一个存放各顶点入度的数组indegree[]

1.扫描indegree[],将入度为0的顶点入栈

2.while(栈非空){

​ 出栈顶元素$v_i$,并输出;

​ 检查$v_i$的出边表,将每条出边<$v_i$,$v_j$>的终点$v_j$的入度减1,若vj的入度减至0,v$v_j$入栈;

}

3.若输出的顶点数小于n则有回路(无法拓扑排序),否则正常结束。

算法实现:

#include <iostream>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define MAX_VERTEX_NUM 20
#define FALSE 0
#define TRUE 1
#define ERROR 0
#define OK 1
using namespace std;
typedef enum {DG,DN,UDG,UDN}GraphKind;
typedef struct InfoType {
int weight;
}InfoType;
typedef struct vertextype{
char id;
int No;
// string label;
}vertextype;
//边表的存储结构
typedef struct ArcNode{
int adjvex; //该弧指向的顶点位置
struct ArcNode *nextarc;
InfoType *info;
}ArcNode;
//顶点表的存储结构
typedef struct vnode{
vertextype data;
ArcNode *firstarc;
}vnode,adjlist[MAX_VERTEX_NUM];
//邻接表的存储结构
typedef struct {
adjlist vertices;
int vexnum,arcnum;
GraphKind kind;
}ALGraph;
void Creatadilist(ALGraph &G){
int i,j,k;
ArcNode *s;
scanf("%d,%d,%d",&G.vexnum,&G.arcnum,&G.kind);
for(i=0;i<G.vexnum;i++){
cin>>G.vertices[i].data.id;
G.vertices[i].firstarc=NULL;
}
for(k=0;k<G.arcnum;k++){
cin>>i>>j;
s=(ArcNode*)malloc(sizeof(ArcNode));
s->adjvex=j;
s->info=NULL;
s->nextarc=G.vertices[i].firstarc;
G.vertices[i].firstarc=s;
}
}
struct SElemType{
int a;
int b;
};
typedef struct{
int *base;
int *top;
int stacksize;
}SqStack;
void InitStack(SqStack &S){
S.base=(int*)malloc(STACK_INIT_SIZE*sizeof(int));
if(!S.base) exit(0);
S.top=S.base;
S.stacksize=STACK_INIT_SIZE;
// cout<<"Init succeed."<<endl;
}
void Push(SqStack &S,int e){
if(S.top-S.base>=S.stacksize){
S.base=(int*)realloc(S.base, (S.stacksize+STACKINCREMENT)*sizeof(int));

if(!S.base) {
// cout<<"ERROR"<<endl;
exit(0);
}
S.top=S.base+S.stacksize;
S.stacksize+=STACKINCREMENT;
}
*S.top++=e;
// cout<<"Push succeed."<<endl;
}
void Pop(SqStack &S,int &e){
if(S.top==S.base){
// cout<<"Stack is Empty."<<endl;
exit(0);
}
else{
e=*(S.top-1);
S.top--;
//cout<<"Pop succeed."<<endl;
}

}
void Clear(SqStack &S){
while(S.top>S.base){
int* p=S.top;
S.top--;
free(p);
S.stacksize-=STACKINCREMENT;
}
// cout<<"Stack clear succeed."<<endl;
}
void DestroyStack(SqStack &S){
if (S.base != NULL ){
Clear(S);
free(S.base);
S.base = NULL;
S.top = NULL;
S.stacksize = 0;
//cout << "Stack destroyed." << endl;
}
}

int StackEmpty(SqStack S){
if(S.base==S.top) return 1;
else return 0;
}
int StackLength(SqStack S){
if(!(S.base==S.top)) return S.top-S.base;
else return 0;
}

void GetTop(SqStack S,int &e){
e=*(S.top-1);
cout<<"e is:"<<(&e)<<"-"<<(&e)<<endl;
}
void StackTravers(SqStack S){
int i=1;
if(StackEmpty(S))
{
cout<<"Stack is Empty."<<endl;
exit(0);
}
while(!(S.top==S.base)){
S.top--;
cout<<i<<". ";
cout<<(*(&S.top))<<endl;
//cout<<(*(&S.top))->b<<endl;
i++;
}
}
void FindInDegree(ALGraph G, int indegree[]){
int i;ArcNode *p;
for(i=0;i<G.vexnum;i++){
p=G.vertices[i].firstarc;
while(p){
indegree[p->adjvex]++;
p=p->nextarc;
}
}
}
int TopologicalSort(ALGraph G){
SqStack S;
int count,k,i;
ArcNode *p;
int indegree[MAX_VERTEX_NUM]={0};
FindInDegree(G, indegree);
InitStack(S);
for(i=0;i<G.vexnum;++i){
if(!indegree[i]) Push(S, i);
}
count=0;
while(!StackEmpty(S)){
Pop(S,i);
printf("%d %d",i,G.vertices[i].data.No);
++count;
for(p=G.vertices[i].firstarc;p;p=p->nextarc){
k=p->adjvex;
if(!(--indegree[k])) Push(S, k);
}
}
if(count<G.vexnum)return ERROR;
else return OK;
}

最短路径

问题提出

路径长度:路径上边的权值之和

最短路径:两结点间权值之和最小的路径

单源最短路径Dijkstra

给定有向图G和源点$v_i$,求$v_i$到G中其余各顶点的最短路径

算法思想

按路径长度的递增次序,逐步产生最短路径Dijkstra算法(SPF算法)

首先求出长度最短的一条路径,再参照它求出长度次短的一条最短路径,以此类推,直到从顶点v到其他各顶点的最短路径全部求出为止

算法要点

例图:单源最小路径
1.设源点$v_0$,顶点集合分成两部分:

U:已经求出最短路径的顶点集合

V-U:未求出最短路径的顶点集合

初值:U={$v_0$},V-U={$v_1$,$v_2$,$v_3$,$v_4$}

第二个加入U中的顶点必然是与$v_0$邻接且与$v_0$之间的边长最短的顶点

2.U={$v_0$,$v_2$},V-U={$v_1$,$v_3$,$v_4$}

设第三个加入U中的顶点是w$\in$V-U,则w到$v_0$的最短路径的特点是:w是V-U中满足$v_0$->w和$v_0$->$v_2$->w中最短的

存储结构
#include <iostream>
#define MAX_VERTEX_NUM 100
typedef struct {
int vexs[MAX_VERTEX_NUM];
float arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int vexnum,arcnum;
}MGraph;
算法概要

1.引入辅助数组D[ ],D[i]表示当前所找到的源点到每个终点i的最短路径长度,最短路径的初值即有向边的权值D[i]=G.arcs[$v_0$] [i]

​ 引入辅助数组final[ ],final[i]=1表示顶点i的最短路径已经求出,否则未求出

​ 初始状态:final[$v_0$]标志为1,其余为0

​ 引入数组P[ ]来记录路径

2.选择D[ ]中路径最小值的顶点v(已经求出最短路的顶点除外)

​ v就是当前求得得一条从$v_0$出发的最短路径的终点,修改final[v]=1

3.修改未求出最短路径的顶点的最短路径长度,如果D[v]+G.arcs[v] [w]<D[w]

​ 则修改D[w],D[w]=D[v]+G.arcs[v] [w]

​ 同时修改P[w]=v

4.重复2 ,3操作 n-1次,求得最短路径长度递增序列

算法实现
//  dijkstra
#include <iostream>
using namespace std;
#define MAX_VERTEX_NUM 100
#define INFINITY 1000
typedef struct ArcCell {
int adj;
}ArcCell,AdjMatrix[MAX_VERTEX_NUM];
typedef struct {
int vexs[MAX_VERTEX_NUM];
// AdjMatrix arcs;
float arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int vexnum,arcnum;
}MGraph;
void ShortestPath_DIJ(MGraph G,int v0,int *P,float *D){
int i=0,v,w,min,f[MAX_VERTEX_NUM];
for(v=0;v<G.vexnum;++v){
f[v]=0;
D[v]=G.arcs[v0][v];

P[v]=-1;
if(D[v]<INFINITY) P[v]=0;
}
f[0]=1;P[0]=-1;
for(int j=0;j<5;j++){
cout<<D[j]<<" ";
}
cout<<endl;
for(int j=0;j<5;j++){
cout<<f[j]<<" ";
}
cout<<endl;
for(int j=0;j<5;j++){
cout<<P[j]<<" ";
}
cout<<endl;
for(i=1;i<G.vexnum;++i){
min=INFINITY;
for(w=0;w<G.vexnum;++w){
if(!f[w])
if(D[w]<min){v=w;min=D[w];}
}
f[v]=1;
for(w=0;w<G.vexnum;++w){
if((!f[w])&&(min+G.arcs[v][w]<D[w])){
D[w]=min+G.arcs[v][w];
P[w]=v;
}
}
}
int pre;
for(i=1;i<G.vexnum;i++){
printf("\n%.0f:%d",D[i],i); //这里因为测试的都用的整数,直接舍去小数输出了
pre=P[i];
while(pre!=-1){
printf("<-%d",pre);
pre=P[pre];
}
}
}
int main(int argc, const char * argv[]) {
MGraph G;
G.vexnum=5;
for(int i=0;i<5;++i){
for(int j=0;j<5;j++){
G.arcs[i][j]=INFINITY;
}
}
G.arcs[0][0]=0;
G.arcs[1][1]=0;
G.arcs[2][2]=0;
G.arcs[3][3]=0;
G.arcs[4][4]=0;
G.arcs[0][1]=6;
G.arcs[0][2]=3;
G.arcs[1][3]=5;
G.arcs[2][1]=2;
G.arcs[2][3]=3;
G.arcs[2][4]=4;
G.arcs[3][4]=2;
float *D = new float[G.vexnum]; // 动态分配内存
int *P = new int[G.vexnum];
ShortestPath_DIJ(G, 0, P,D);
delete[] D; // 释放内存
delete[] P;
}

算法分析

Dijkstra算法的时间复杂行主要体现在求每个顶点的最短路径是,需要修改距离和求最小值,时间复杂性O($n^2$)

Dijkstra算法的空间复杂性主要体现在辅助数组,空间复杂性为O(n)

每一对顶点之间的最短路径

依次把有向网络的每个顶点作为源点,重复执行Dijkstra算法n次,即可求得每对顶点之间的最短路径

Floyd算法 可以直接求出所有顶点之间的最短路径

算法思想

对顶点进行编号,设顶点为0,1,…,n-1,算法采用邻接矩阵G.arcs[n] [n]表示有向网络

$D^{(-1)}[n][n]$表示中间不经过任何点的最短路径;即邻接矩阵

$D^{(0)}[n][n]$表示只允许经过0号顶点的最短路径

$D^{(1)}[n][n]$表示只允许经过0号和1号顶点的最短路径

$…….$

$D^{(n-1)}[n][n]$表示可经过所有顶点的最短路径

基本想法:动态规划算法

如果$v_i$与$v_j$之间有一条路径,但不一定最短,也许经过某些中间点会使路径长度更短,尝试在原路径中加入其他顶点作为中间顶点