图(graph):结点之间的关系可以是任意的,图中任意两个数据之间都可能相关。二元组定义:图G是一个二元组(V,E),其中V称为顶点集(Vertexes Set);E称为边集(Edges Set)。有向图:VR是两个顶点之间的关系集合;若,则表示从v到w的一条弧(arc),且称v为弧尾(tail)或初始点(initialnode),称w为弧头(head)或终端点(terminal node),此时的图称为有向图(Digraph)。无向图:若必有,即VR是对称的,则以无序对(v,w)代替这两个有序对,表示v和w之间的一条边(edge),此时的图称为无向图(Undigraph)。
1.完全图(completed graph):有条边的无向图。有向完全图:具有条弧的有向图。2.稀疏图(sparse graph):有很少条边或弧的图;反之称为稠密图(densegraph)。3.权(weight):图的边或弧具有与它相关的数称为权。网(network):权可以表示从一个顶点到另一个顶点的距离或耗费,这种带权的图称为网。4.邻接点(adjacent):对于无向图,如果边,则称顶点v和w互为邻接点;顶点v的度(degree)是:和v相关联的边的数目,记为TD(v)。对于有向图,如果弧,则称顶点v邻接到顶点w,顶点w邻接自顶点v;以v为头的弧的数目称为v的入度(InDegree),记为ID(v);以v为尾的弧的数目称为的出度(OutDegree),记为OD(v);则顶点v的为TD(v)=ID(v)+OD(v)。度和有n个顶点、e个边或弧的图的关系:。5.连通图(connected graph):对于无向图G中任意两个顶点、,从到有路径;则称G是连通图。对于有向图G任意两个顶点、,,从到和从到都存在路径;则称G是强连通图。6.生成树:一个连通图的生成树是一个极小连通子图,它含有图中全部顶点,但只有足以构成一颗树的n-1条边。一棵有n个顶点的生成树有且仅有n-1边;若一个图有n个顶点和小于n-1条边,则是非连通图;如果它多于n-1条边,则一定有环;但是有n-1条边的图不一定是生成树(对于连通图,遍历图过程中经历的所有边的集合和图中所有顶点一起构成连通图的极小连通子图;它是连通图的一颗生成树,并且称由深度优先搜索得到的为深度优先生成树;由广度优先搜索得到的为广度优先生成树。对于非连通图,每个连通分量中的顶点集,和遍历时走过的边一起构成若干棵生成树,这些连通分量的生成树组成非连通图的生成森林)。最小生成树(minimum cost spanning tree):构造连通网络的最小代价生成树。7.存储结构:由于图的结构比较复杂,任意两个顶点之间都可能存在联系,因此无法以数据在存储区中的物理位置来表示元素之间关系,即图没有顺序映像的存储结构,但可以借助数组的数据类型表示元素之间的关系。另一方面,用多重链表表示图是自然的事,它是一种最简单的链式映像结构,即以一个由一个数据和多个指针域组成的结点表示图中的一个顶点,其中数据域存储该顶点的信息,指针域存储指向其邻接点的指针(结点的度不同造成结点设计不同)。1)数组表示(邻接矩阵):以二维数组表示有n个顶点的图时,需要存放n个顶点和n2个弧信息的存储量。2)邻接表(adjacencylist):是图的一种链式存储结构。在邻接表中,对图中每个结点建立一个单链表,第i个单链表中的结点表示依附于顶点vi的边(对有向图是以顶点vi为尾的弧)。表结点由3个域组成:邻接点域指示与顶点vi邻接的点在图的位置;链域指示下条边或弧的结点;数据域存储和边或弧相关的信息。头结点:链域指向链表中第一个结点;数据域存储顶点名或其他信息。头结点通常以顺序结构的形式存储,以便随机访问任一顶点的链表。3)十字链表(orthogonal list):是有向图的另一种链式存储结构。可以看成是将有向图的邻接表和逆邻接表结合起来得到的一种链表。在十字链表中,对应于有向图中每条弧有一个弧结点,对应于每个顶点也有一个头结点。弧结点:其中尾域(tailvex)和头域(headvex)分别指示弧尾和弧头这两个顶点在图中的位置;链域hlink指向弧头相同的下一条弧,而链域tlink指向弧尾相同的下一条弧;info域指向该弧的相关信息。顶点结点:data域存储和顶点相关的信息,firstin和firstin为两个链域,分别指向以该顶点为弧头或弧尾的第一个弧结点。4)邻接多重表(adjacency multilist):是无向图的另一种链式存储结构。在邻接多重表中,每一条边用一个边结点表示,每一个顶点也用一个头结点表示。边结点:mark为标志域,可用以标记该条边是否被搜索过;ivex和jvex为该边依附的两个顶点在图中的位置;iline指向下一条依附于顶点ivex的边,jlink指向下一条依附于顶点jvex的边;info为指向和边相关的信息的指针域。头结点:data域存储和该顶点相关的信息;firstedge域指示第一条依附于该顶点的边。8.图的遍历(traverse graph):从图中某个顶点出发遍历图中其余顶点,且使每一个顶点仅被访问一次。为避免同一顶点被访问多次访问,为此设置一个数组visited[n]。两条遍历图的路径:深度优先搜索和广度优先搜索。它们对无向图和有向图都适用。(1)深度优先搜索(DepthFirst Search,DFS):又称回溯法,其采用了一种“一直向下走,走不通就掉头”的思想;类似于树的先根遍历,是树的先根遍历的推广。用邻接表存储时时间复杂度为O(v+e)。
基本思路:从图的某个顶点V出发,访问此顶点;然后依次从V的未被访问的邻接点出发深度优先遍历图,直至图中所有和V有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问到,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止(有递归和非递归实现的算法-利用栈stack来实现)。DFS对于寻找一个解的NP问题作用很大,提高深度优先搜索的效率最具通用性的是剪枝(prunning)即去除没有用的搜索分支。(2)广度优先搜索(BreadthFirst Search):类似于树的按层次遍历的过程。其过程是以v为起始点,由近及远,依次访问和v有路径相通且路径长度为1,2,3…顶点。用邻接表存储时时间复杂度为O(v+e)。基本思想:从图中的某顶点出发,在访问了V之后依次访问V的各个未曾访问过的邻接点;然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问;直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始,重复上述过程,直至图中所有顶点都被访问到为止(利用队列queue实现)。应用在最短路径上较多。9.拓扑排序(topologicalsort):对于一个有向无环图,将其所有顶点排成一个线性序列,使得图中任意一对顶点v和u,若弧,则v在线性序列中出现在v之前。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,且这个全序称为拓扑有序。AOV-网(activity on vertex network):用顶点表示活动,用弧表示活动间的优先关系的有向图称为顶点表示活动的网。拓扑排序方法:1)在有向图中选一个没有前驱(入度为零)的顶点且输出之。2)从图中删除该顶点和所有以它为尾的弧。重复上述两步直至全部顶点均已输出;或者当前图中不存在无前驱的顶点为止(有向图中存在环)。AOE-网(activity on edge):用顶点表示事件,弧表示活动,权表示活动持续的时间的带权的有向无环图称为边表示活动的网。10.最短路径(shortest path):一个顶点到另一个顶点的最短路径(路径上边的数目/路径上边的权值之和)。(1)单源最短路径:从某个源点到其余各顶点的最短路径。1)Dijkstra算法:又称SPF(shortest path first最短路径优先)算法;求不含有负权环图的单源最短路径。算法思想:设是一个带权有向图,把图中顶点集合V分成两组:第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径,就将加入到集合S中,直到全部顶点都加入S中为止);第二组为其余未确定最短路径的顶点集合(用U表示)。按最短路径长度的递增次序依次把第二组U中的顶点加入S中;在加入的过程中,总保持从源点到S中各顶点的最短路径长度不大于从源点到U中任何顶点的最短路径长度。从而求得从到图上其余各个顶点的最短路径是路径长度递增的序列(即D[v])。时间复杂度为。2)Bellman-Ford算法:求含负权图的单源最短路径。时间复杂度为。3)SPFA(shortestpath faster algorithms)算法:求含有负权环的图单源最短路径算法。是在bellman-ford算法的基础上加上一个队列优化,减少了冗余的松弛操作。时间复杂度为。注:松弛操作(relaxation):对于每个顶点,都设置一个属性d[v],用来描述从源点s到v的最短路径权值的上界。一次松弛操作可以减少最短路径估计的值d[v],并更新当前最短路径。(2)多源最短路径:每一对顶点之间的最短路径。Floyd算法:又称插点法;是一种动态规划算法,边权可正可负。算法思想:若(vi,… ,vk)和(vk , … ,vj)分别是从vi到vk和从vk到vj的中间顶点的序号不大于K-1的最短路径,则将(vi, … ,vk , …vj)和已经得到的从vi到vj且中间顶点序号不大于K-1的最短路径相比较,其长度较短者便是从vi到vj的中间顶点的序号不大于k的最短路径。这样比较n次后最后求得的必是从vi到vj的最短路径。时间复杂度。


#include <stdlib.h>
#include <iostream>
#include <queue>
#define MAX_VERTEX_NUM8

using namespacestd;
enum GraphKind{DG,DN,UDG,UDN};//{有向图,有向网,无向图,无向网}
typedef structArcNode{
         intadjvex; //该弧所指向的顶点或边的另一个顶点的位置
         ArcNode *nextArc;//指向下一条弧或边的指针
         //infoType*info; //该弧/边相关信息的指针
}ArcNode; //表/弧/边结点
typedef struct{
         chardata; //顶点信息
         ArcNode *firstArc;//第一个表结点的地址,指向第一条依附该顶点的弧或边指针
}VNode; //头结点
typedef struct{
         VNode headArray[MAX_VERTEX_NUM];//头结点数组
         intvexNum, arcNum; //图的当前顶点数和弧/边数
         GraphKind kind;//图的种类标志
}Graph; //图
//创建一个固定的图
Graph* createGraph(){
         Graph* graph = (Graph*)malloc(sizeof(Graph));
         graph->vexNum= 8;
         graph->arcNum= 18;
         graph->kind= UDG;
         for(inti = 0; i <MAX_VERTEX_NUM; i++)
                   graph->headArray[i].data= 'A' + i;
         //为每个表/弧/边结点分配空间Pnm表示以第n+1个为顶点的第m+1个弧/边j结点
         ArcNode *p00 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p01 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p10 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p11 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p12 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p20 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p21 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p22 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p30 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p31 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p40 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p41 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p50 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p51 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p60 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p61 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p70 = (ArcNode*)malloc(sizeof(ArcNode));
         ArcNode *p71 = (ArcNode*)malloc(sizeof(ArcNode));
         //为表/弧/边结点的属性赋值
         p00->adjvex= 1;  //该弧所指向的顶点或边的另一个顶点的位置
         p00->nextArc= p01; ///指向下一条弧或边的指针
         p01->adjvex= 2;
         p01->nextArc= NULL;
         p10->adjvex= 0;
         p10->nextArc= p11;
         p11->adjvex= 3;
         p11->nextArc= p12;
         p12->adjvex= 4;
         p12->nextArc= NULL;
         p20->adjvex= 0;
         p20->nextArc= p21;
         p21->adjvex= 5;
         p21->nextArc= p22;
         p22->adjvex= 6;
         p22->nextArc= NULL;
         p30->adjvex= 1;
         p30->nextArc= p31;
         p31->adjvex= 7;
         p31->nextArc= NULL;
         p40->adjvex= 1;
         p40->nextArc= p41;
         p41->adjvex= 7;
         p41->nextArc= NULL;
         p50->adjvex= 2;
         p50->nextArc= p51;
         p51->adjvex= 6;
         p51->nextArc= NULL;
         p60->adjvex= 2;
         p60->nextArc= p61;
         p61->adjvex= 5;
         p61->nextArc= NULL;
         p70->adjvex= 3;
         p70->nextArc= p71;
         p71->adjvex= 4;
         p71->nextArc= NULL;
         //将顶点与每个链表连接起
         graph->headArray[0].firstArc= p00;
         graph->headArray[1].firstArc= p10;
         graph->headArray[2].firstArc= p20;
         graph->headArray[3].firstArc= p30;
         graph->headArray[4].firstArc= p40;
         graph->headArray[5].firstArc= p50;
         graph->headArray[6].firstArc= p60;
         graph->headArray[7].firstArc= p70;
         returngraph;
}

int visited[MAX_VERTEX_NUM]= {0};//全局变量用来保存结点的访问信息
/*
* DFSTraverse
*函数功能:又称回溯法,其采用了一种“一直向下走,走不通就掉头”的思想;
*          类似于树的先根遍历,是树的先根遍历的推广
*输入:图graph
*返回值:void
*/

//DFS:Depth First Search
void DFS(Graph*graph,intv){
         cout<< graph->headArray[v].data;
         visited[v]= 1;
         //对于无向图,可以遍历到v顶点所在的连通分量中的所有顶点;
         //而对于有向图可以遍历到起始顶点v能够到达的所有顶点
         ArcNode* nextArc =graph->headArray[v].firstArc;//顶点v的第一条弧/边
         for(intw = nextArc->adjvex; nextArc!=NULL&& w >= 0; nextArc = nextArc->nextArc){
                   w= nextArc->adjvex; //顶点v的下一个邻接点
                   if(!visited[w])
                            DFS(graph,w);//对v的尚未访问的邻接点w递归调用DFS
         }
}

void DFSTraverse(Graph*graph){
         for(intv = 0; v <graph->vexNum;v++){
                   if(!visited[v])
                            DFS(graph,v);
         }
}

/*
* BFSTraverse
*函数功能:类似于树的按层次遍历的过程。其过程是以v为起始点,由近及远,
*          依次访问和v有路径相通且路径长度为1,2,3…顶点。
*          用邻接表存储时时间复杂度为O(v+e)
*输入:图graph
*返回值:void
*/

//BFS:Breadth First Search
void BFS(Graph*graph,intv){
         queue<int> q;
         cout<< graph->headArray[v].data;
         visited[v]= 1;
         q.push(v);
         inti;
         while(!q.empty()){
                   i= q.front();
                   q.pop();
                   ArcNode* nextArc =graph->headArray[i].firstArc;//顶点i的第一条弧/边
                   for(intw = nextArc->adjvex; nextArc !=NULL&& w >= 0; nextArc = nextArc->nextArc){
                            w= nextArc->adjvex; //顶点v的下一个邻接点
                            if(!visited[w]){
                                     cout<< graph->headArray[w].data;
                                     visited[w]= 1;
                                     q.push(w);
                            }
                   }
         }
}

void BFSTraverse(Graph*graph){
         for(inti = 0; i <graph->vexNum;i++){
                   if(!visited[i])
                            BFS(graph,i);
         }
}
/*
*shortestPathDIJ
*函数功能:求从v0到其余顶点v的最短路径
*输入:顶点个数n,源顶点v0
*返回值:void
*/
int arcs[6][6] = {1000000,1000000,10,100000, 30 ,100,
1000000,1000000, 5 ,1000000, 1000000, 1000000,
1000000 ,1000000 ,1000000, 50 ,1000000 ,1000000,
1000000 ,1000000, 1000000, 1000000, 1000000, 10,
1000000 ,1000000, 1000000 ,20 ,1000000, 60,
1000000 ,1000000 ,1000000, 1000000 ,1000000, 100000}; //邻接矩阵
int path[6][6]; //path[v][w]=1,则w是从v0到v当前求得最短路径上的顶点
                      //path[v]是从v0到其余顶点v的最短路径
int D[6]; //从v0到其余顶点v的最短路径的带权长度D[v]
int final[6]; //final[v]=1,顶点v在集合S中;已经求得从v0到v的最短路径
void shortestPathDIJ(intn,intv0){
         intv, w;
         //初始化
         for(v = 0; v < n;v++){
                   final[v]= 0;
                   D[v]= arcs[v0][v];
                   for(w = 0; w <n; w++)
                            path[v][w]= 0; //设空路径
                   if(D[v] < 1000000){//1000000代表无穷,说明从v0到v有直接路径
                            path[v][v0]= 1;
                            path[v][v]= 1;
                   }
         }
         D[v0]= 0;
         final[v0]= 0; //初始化v0顶点属于集合S
         //每次求得v0到某个v顶点的最短路径,并加v到S集
         for(inti = 0; i <n;i++){
                   intmin = 1000000;
                   for(w = 0; w <n;w++)
                            if(!final[w]){ //w顶点V-S中
                                     if(D[w]<min){
                                               v= w;
                                               min= D[w]; //w顶点离v0顶点更近
                                     }
                            }
                   final[v]= 1; //离v0顶点最近的v加入S集
                   for(w = 0; w <n;w++) //更新当前最短路径及距离
                            if(!final[w]&&(min+arcs[v][w])<D[w]){
                                     D[w]= min + arcs[v][w];
                                     *path[w]=* path[v]; //使path[w]= path[v];
                                     path[w][w]= 1;
                            }
         }
}

/*
*shortestPathFloyd
*函数功能:求每一对顶点的最短路径
*输入值:邻接矩阵的顶点数n
*返回值:void
*/
int Path[6][6][6]; //若Path[v][w][u]=1;则u是从v到w当前求得最短路径上的顶点
                 //Path[v][w]是各对顶点从v到w的最短路径
int Distance[6][6]; //从v到w的最短路径的带权长度D[v][w]
void shorestPathFloyd(intn){
         intv, w, u;
         //初始化各对结点之间初始已知路径及距离
         for(v = 0; v < n; v++)
                   for(w = 0; w <n; w++){
                            Distance[v][w]= arcs[v][w];
                            for(u = 0; u <n; u++)
                                     Path[v][w][u]= 1;
                            if(Distance[v][w] < 1000000){//1000000代表无穷,说明从v到w有直接路径
                                     Path[v][w][v]= 1;
                                     Path[v][w][w]= 1;
                            }
                   }

         for(u = 0; u < n; u++) //u为从v到w经过的中间点
                   for(v = 0; v <n; v++)
                            for(w = 0; w <n;w++)
                                     if(Distance[v][u] + Distance[u][w] < Distance[v][w]){//更新当前最短路径及距离
                                               Distance[v][w]= Distance[v][u] + Distance[u][w];
                                               for(inti = 0; i < n; i++)
                                                        Path[v][w][i]= Path[v][u][i] || Path[u][w][i];
                                     }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值