提示:身着白衣,心有锦缎
文章目录
前言
这里我写的不是本人看的话 想来大家也是看不懂我写的什么鬼 这里大家自己根据我之前所写 希望大家自己也写一下 才有用
本节中就图的创建 遍历 几种常见的算法进行 回顾
详情还是请看之前的**图的专题
提示:以下是本篇文章正文内容,下面案例可供参考
一、图的创建
邻接矩阵
首先需要知道图的存储形式分为三种,邻接矩阵,邻接表,十字链表 等 但是主要考察的话主要是邻接矩阵 邻接表
首先一个图 需要点和边 其中点存储在一个一维数组中,边存储在一个二维数组中 便是邻接矩阵 若是边存储在链表中便是邻接表 这里我们写一下存储的结构体
邻接矩阵
typedef struct Graph{
VerTexType vexs[MVNum];//顶点表
ArcType arcs[MVNum][MVNun];//邻接矩阵
int vexnum;//顶点数
int arcnum;//边数
}Graph;
邻接表
图中需要有边 和点的类型 边我们需要重新定义一个结构体,需要带一个指针,带指针的用处是 它需要靠这个指针来寻找下一个边,并且需要带此边的权重 还有就是需要知道此边指向的是那一个结点, 基于此我们写出边的结构
typedef struct Arc{
int info;//存储数据
int NextVex;//存放这个边所指向的点
Arc* next; //存放与这个边相同起点的下一个边的指针
}Arc;
此时我们来看点 点此时需要存储它本来的名字信息,除此之外还有它所连接的出度 所以也就需要一个指针
typedef struct Vex{
char name;//结点的名字
Arc* next;//存储的边的信息
}Vex;
现在有边 有点就可以创建图了
typedef struct Graph{
int arcnum;
int vexnum;
Vex Vertices[MAX];
};
使用邻接矩阵创建一个图
既然前面我们存储方式知道了 自然创建图也就是往里面填数据 首先我们先填点表,点表也就是一个一维数组,将点输入之后就是就可以输入边,但是输入的时候,你并不知道边所对应的点是什么,所以需要先查查找边对应的横竖坐标,找到之后 我们这里存储的无向图,所以也就需要A[i][j]与A[j][i] 都需要填上信息
使用邻接表创建一个图
首先还是先将点表填充,与邻接矩阵中的点表不同是这个点表中也有指针域,点填充之后 下面开始填充边 这里需要知道的起点与终点 我们是将按起点插入哪一个点之后的链表中,拿到起点之后 我们就到点表中找它对应的点在点表中的位置,找到之后 申请一个结点,将此边结点插入对印的链表中 这里建议头插法,
十字链表
通过上面的建立发方式我们知道了,若是找一个结点的出度 之间查找对应的点表即可,若是要查找出度,则需要遍历每一个结点对应的所有链表,所有想到一个策略 不止将结点出度连接 同时在点表中添加一个入度 每一次进来一个边的时候,不仅按照之前那样将边结点按照头插发插入 而且还需将它对印的出度结点也按照头插法插入,
深度广度
邻接矩阵
深度优先就是目前来到这个结点,我们继续沿着这个结点深入,直到这个结点到头了 就看是否它的邻接的是否都遍历过了 若是遍历过 就开始回溯 所以这里可以想到的一个方式就是递归调用 自然就会回溯
void DFS(Graph1* &G,int i){
for(int j=0;j<G.vexnum;j++){
if(visit[i]==false&&G.arcs[i][j]!=0){//此时存在边 且边没有被访问过
visit[i]=true;
cout<<G.vexs[j]<<" ";
DFG(G,j);
}
}
}
邻接表深度优先
同样的道理 先将遍历到这一个边的最深处,然后再回溯 这里我们使用这里写的是进入这个结点之前 ,这个结点已经被访问过 然后我们来到这个结点对应的边 进入while之前先判断这个是否存在边,若是存在则找这个边的出结点是否被访问过,若是没有被访问过则进入 若是被访问过则看此结点连接的下一个几点 若是没有被访问过 则此时将其设置为访问过,并且递归 也就是深入
void DFS(Graph2* &G,int i){
Arc* cur=G.Vertices[i].next;
while(cur){
if(visit[cur->NextVex]==false){
visit[cur->NextVex]=true;
cout<<G.Vertices[cur->NextVex]<<" ";
DFS(G,cur->NextVex);
}
cur=cur->next;
}
}
广度优先
邻接矩阵
之前树的时候 层序遍历就是广度遍历,当时是借助队列来实现的,这里同样我们也是借助栈来实现图的广度优先 每进入一个结点都是将与他相连的结点都放入队列中
这里队列中存放的都是已经访问过的结点,并且再进入队列的时候 我们就访问了
void BFS(Graph1* &G,int i){
if G==NULL;Q.push(i);visit[i]=true;
while(!Q.empty()){
int cur=Q.front();
for(int i=0;i<G.vexnum;i++){
if(G.arcs[cur][i]!=0&&visit[i]==false){
cout<<G.vexs[i]<<" ";
G.visit[i]=true;
Q.push(i);
}
}
Q.pop();
}
}
邻接表
思想是一样的,但是因为存储方式的不同 导致操作起来也不同
queue<int> Q;
void BFS2(Graph2* &G,int i){
Q.push(i);visit[i]=true;
while(!Q.empty()){
int cur=Q.front();//每次从队列取出一个点
Arc* CurNode=G.Vertices[i].next;
while(CurNode){//将这个点所连接的点放入队列中
if(visit[CurNode->NextVex]==fasle){
cout<<G.Vertices[i].name<<" ";
visir[CurNode->NextVex]=true;
Q.push(CurNode->NextVex);
}
CurNode=CurNode->next;
}
Q.pop();
}
}
迪杰斯特拉
迪杰斯特拉是用来求单源最短路径的,也就是一个点到其他点的最短距离是多少,并且不适合有负权的,其中的关键是理解dict[k]+A[k][j]<dict[j] 表示的含义是 若是新加入这个点K之后 从i 到j的路径若是更短了,则需要更新dict[j]数组 更新之后的dict[j]=dict[k]+A[k][j];每一次可以从其中找到一个结点加入最短路径中 所以也就需要n-1也遍历也就可以
首先初始话dict数组,就是其到源点的距离 若是为零则赋值为无穷,还有就是初始化visit[] 数组,将其本身置为true; 开始进行选择 从此时dict 中寻找一个最小值,然后将此最小值K,然后来更新数组dict[] 若是dict[j]>dict[k]+A[k][j];则更新dict[],,直到循环执行n-1此也就将这n-1个结点选择出来了
/*迪杰斯特拉算法*/
void distl(Graph1 *G,int i){
bool visit[G.vexnum]=false;visit[i]=true;
int dict[G.vexnum];
for(int i=0;i<G.vexnum;i++){//初始化dict数组
if(G.arcs[0][i])dict[i]=G.arcs[0][i];
else dict=Max;
}
dict[i]=0;
//以上是对dict以及visit数组的初始化
for(int i=0;i<G.vexs-1;i++){//需要选择n-1次结点
//先从dict 中选择一个最小的 并且没有访问过的
int min=Max;int MinLoc=0;
for(int j=0;j<G.vexnum;j++){
if(dict[j]<min&&visit[j]==false){
min=dict[j];//一定可以找到 因为要进行n-1次才能找到所有的结点
MinLoc=j;
}
}
visit[MinLoc]=true;
//按照这个最小的来更新dict数组
for(int j=0;j<G.vexnum;j++){
if(dict[MinLoc]+G.arcs[MinLoc][j]<dict[j]){
dict[j]=dict[MinLoc]+G.arcs[MinLoc][j];
}
}
}
}
prim 算法
Prim算法是为了求最小生成树,为什么写在这里 是因为这两个几乎一样,基本思想是将所有的结点分成两种结点,一种是已经在树中的,一种不在树中的 ,每一次从不在树中的结点中选择一个距离此时树最小的一个结点K加入树中,然后根据这个新加入树中的结点,来更新dict数组 你要理解这两个dict数组的含义是不一样的,此prim中的dict 表示的是非树结点距离树的最短距离,如果dict[j]>arc[j][k] 此时就更新dixt[j]=arc[j][k];
/*Prim算法*/
void distl(Graph1 *G,int i){
bool visit[G.vexnum]=false;visit[i]=true;
int dict[G.vexnum];
for(int i=0;i<G.vexnum;i++){//初始化dict数组
if(G.arcs[0][i])dict[i]=G.arcs[0][i];
else dict=Max;
}
dict[i]=0;
//以上是对dict以及visit数组的初始化
for(int i=0;i<G.vexs-1;i++){//需要选择n-1次结点
//先从dict 中选择一个最小的 并且没有访问过的
int min=Max;int MinLoc=0;
for(int j=0;j<G.vexnum;j++){
if(dict[j]<min&&visit[j]==false){
min=dict[j];//一定可以找到 因为要进行n-1次才能找到所有的结点
MinLoc=j;
}
}
visit[MinLoc]=true;
//按照这个最小的来更新dict数组
for(int j=0;j<G.vexnum;j++){
if(G.arcs[MinLoc][j]<dict[j]){
dict[j]=G.arcs[MinLoc][j];
}
}
}
}
细心的同学可能发现一个问题 就是我没有在dict数组判断的那个语句中加上visited[j]==false 上面一个迪杰斯特拉我也同样的没有加 希望大家自己开动小脑袋 想想什么情况下会出现visit[i]==false 并且更新了dic[j]数值
贝尔曼福特
实不相瞒 这个困惑了我一天 主要是昨天晚上多想了一些 我在想若是迪杰斯特拉不加visit[i]==false 那么我在运行 那么不就是可以处理负权的问题了吗 结果果然和我想的一样 结果我就迷茫了 也问了几个人 结果都不会 服了!!!,那么问题先搁置,基本原理是 逐遍的对图中的每一个遍去迭代计算起始点到其余各个点的最短路径,执行N-1遍,最终得到起始点到各个点之间的最短路径 这里还是理解为深度,开始的时候dict已经处理一次过了,所以还需要n-2次松弛这就是最外层的for 判断所有的此深度下的通过中转结点 j 到达的终点k 是否更好 这个深度最多不过是n-1之前我们初始化的时候已经搞过一次 这里需要n-2 次 就这样蒙吧
弗洛伊德
首先弗洛伊德解决的问题是 任意两点中的距离 并且要求权值不能为负,基本思想是若是两个结点i 与j中存在k 结点使得路径更短,我们就更新dict[i][j]=dict[i][k]+dict[k][i] 数组 并且记录下此时的通过的结点path[i][j]=vec[k];
克鲁斯卡尔
与之前的prim prim 是通过顶点来扩展生成树,每一次都是从距离此时树最小距离的结点中选择一个,,但是克鲁斯卡尔不同,它的思想是,按边来选择, 所以我们需要先将边按照权值大小进行排序,但是按照权值排序,按照原来无论是矩阵存储 或者是邻接表的形式都是不易找此边所连接的结点,所以这里我们使用一个新的结构体 ,还有就是需要对结构体中的变量进行排序 注意一下 排序的方法 判断的时候是看一个边的两个结点是否是属于同一个树,若是同一个名字 直接返回 若是不同名字 则放入树中 并且将树种的名字都改了
int klsker(Graph1* &G){
//先给每一个结点起一个名字 防止形成闭环
int name[vexnum];
for(int i=0;i<G.vexnum;i++){
name[i]=i;
}
//首先对边排序 也就需要一个新的边的结构 因为是结构体排序 也要一个新的排序方式
vector<edge> E;
E.sort(E.begin(),E.end(),cmp);//从小到大排序
int num=0;//记录此时选择了几个边
int sum=0;//记录此时树的最小权值之和
for(int i=0;i<G.vexnum;i++){
if(num==G.vexnum-1) break;
//遍历所有的边 从中选择两个小的
int falg1=name[E[i].from];
int falg2=nane[E[i].to];
//判断这两个边是否是同一个树中的
if(flag1==falg2){//同一个树
continue;
}
else{
num+=1;
sum+=E[i].info;//将后面那个边纳入到树中
for(int j=0;j<G.vexnum;j++){//将E2 子树的名字都修改
if(name[j]==flag2){
name[j]=flag1;
}
}
}
}
return sum;
}
拓扑排序
活动安排 要求不能具有回路
首先我们大概需要知道一下它实现的方式 其实就是两步,从图章选择一个入度为零的结点,然后删除此结点 已经此以此结点为起点的弧 这个时候也就影响了 其他结点的入度 所有此时也就需要修改其他节点的入度,这里通过邻接矩阵的列来寻找那个入弧结点,然后修改结点对应的 count[]数组,若是结点被删除则将其中的count 置为-1;
void tuopu(Graph1* &G){
int count[G.vexnum]={0};
//开始初始化count数组 其中的数值表示结点的入度
for(int i=0;i<G.vexnum;i++){//表示列
for(int j=0;j<G.vexnum;j++){//表示行
if(G.arcs[j][i]!=0){//说明有值
count[i]++;
}
}
}
for(int i=0;i<G.vexnum;i++){//需要选择这么多次
//每一次从中选择一个结点count值为0的结点
for(int j=0;j<G.vexnum;j++){//从count数组中选择一个cont值为零的
if(conut[j]==0){
count[j]=-1;//将其删除
for(int h=0;h<G.vexnum;h++){//删除j这一列中连接的结点的入度
if(G.arcs[h][j]){
count[h]--;
}
}
break;
}
}
if(j==G.vexnum){
cout<<"存在环"<<endl;
}
}
}
第二种拓扑
这种方式的思路就是 首先依然是初始化count 数组 将其放入栈或者队列中, 甚至我想的话 放入向量中也该也可以,统计之后遍历count 数组将其中为零的结点放入栈中 然后将栈顶元素弹出,弹出之后 依然是修改与之关联的count 然后将这次过程中的count 为零的点也放入栈中,所以这种出栈操作应该要执行n 次 若是少于n次栈就空了 说明此时图中必然是存在回路,这方式的优点是每一次不需从中再挑选count 为零的点
void tuopu(Graph1* &G){
int count[G.vexnum]={0};stack<int> S;
//开始初始化count数组 其中的数值表示结点的入度
for(int i=0;i<G.vexnum;i++){//表示列
for(int j=0;j<G.vexnum;j++){//表示行
if(G.arcs[j][i]!=0){//说明有值
count[i]++;
}
}
if(count[i]==0){
S.push(i);
}
}
int num=0;//记录弹出结点的次数
while(!S.empty()){
num++;
int cur=S.top();
S.pop();//将栈顶弹出
count[cur]=-1;//将其删除
for(int i=0;i<G.vexnum;i++){
if(G.arcs[i][cur]) count[i]--;
if(count[i]==0){
S.push(i);
}
}
}
if(num<G.vexnum){
cout<<"此时是有环的存在的";
}
}
若是使用邻接表来操作 其中都不需要改什么 依然栈中存放整数类型便可
汇总
#include<bits/stdc++.h>
using namespace std;
#define MaxDot 100
#define IntMax 333
//邻接矩阵的存储结构
typedef struct AdjoinMatrix{
int Dotnum;
int Edgenum;
char Dot[MaxDot];
int Edge[MaxDot][MaxDot];
}AdjoinMatrix;
//为了克鲁斯卡尔算法所从新定义边结构
typedef struct KEdge{
int from;
int to;
int info;
}KEdge;
bool visit[MaxDot]={false};
bool visit2[MaxDot]={false};
//邻接表的存储结构
//边结构
typedef struct Edge{
int info;
int Dot;
Edge* next=NULL;
}Edge;
//点结构
typedef struct Dot{
char name;
Edge* FirNext=NULL;
}Dot;
//边点结构汇集成图
typedef struct AdjoinChart{
int Dotnum;
int Edgenum;
Dot DotArray[MaxDot];
}AdjoinChart;
//十字链表
//边结构
typedef struct TenEdge{
int info;
char DotIn;
char DotOut;
TenEdge* In=NULL;
TenEdge* Out=NULL;
}TenEdge;
//点结构
typedef struct TenDot{
char name;
TenEdge* InNext=NULL;
TenEdge* OutNext=NULL;
}TenDot;
//边点结构汇集成图
typedef struct TenAdjoinChart{
int TenDotnum;
int TenEdgenum;
TenDot TenDotArray[MaxDot];
}TenAdjoinChart;
void CreatMatrixGraph(AdjoinMatrix &G){
cout<<"请输入顶点数"<<endl;
cin>>G.Dotnum;
cout<<"请输入顶点"<<endl;
for(int i=0;i<G.Dotnum;i++){
cin>>G.Dot[i];
}
cout<<"请输入边的个数"<<endl;
cin>>G.Edgenum;
for(int i=0;i<G.Edgenum;i++){
cout<<"请输入边对应的点"<<endl;
char input1;cin>>input1;
char input2;cin>>input2;
int cur1=0;while(input1!=G.Dot[cur1++]);
int cur2=0;while(input2!=G.Dot[cur2++]);
cout<<"请输入此边对应的权值"<<endl;
cin>>G.Edge[--cur1][--cur2];
G.Edge[cur2][cur1]=G.Edge[cur1][cur2];
}
}
//创建一个邻接表
void CreatChartGraph(AdjoinChart &G){
cout<<"请输入顶点数"<<endl;
cin>>G.Dotnum;
cout<<"请输入顶点"<<endl;
for(int i=0;i<G.Dotnum;i++){
cin>>G.DotArray[i].name;
}
cout<<"请输入边的个数"<<endl;
cin>>G.Edgenum;
for(int i=0;i<G.Edgenum;i++){
cout<<"请输入边的两个结点 注意方向!!!"<<endl;
char input1;cin>>input1;
char input2;cin>>input2;
int cur1=0;while(input1!=G.DotArray[cur1++].name);
int cur2=0;while(input2!=G.DotArray[cur2++].name);
cout<<"请输入此边对应的权值"<<endl;
Edge* edge=(Edge*)malloc(sizeof(Edge));
cin>>edge->info;edge->next=G.DotArray[--cur1].FirNext;
G.DotArray[cur1].FirNext=edge; edge->Dot=--cur2;
}
}
//创建一个十字链表
void CreatTenAdjoinGraph(TenAdjoinChart &G){
cout<<"请输入顶点数"<<endl;
cin>>G.TenDotnum;
cout<<"请输入顶点"<<endl;
for(int i=0;i<G.TenDotnum;i++){
cin>>G.TenDotArray[i].name;
}
cout<<"请输入边的个数"<<endl;
cin>>G.TenEdgenum;
for(int i=0;i<G.TenEdgenum;i++){
cout<<"请输入边的两个结点 注意方向!!!"<<endl;
char input1;cin>>input1;
char input2;cin>>input2;
int cur1=0;while(input1!=G.TenDotArray[cur1++].name);
int cur2=0;while(input2!=G.TenDotArray[cur2++].name);
cout<<"请输入此边对应的权值"<<endl;
TenEdge* edge=(TenEdge*)malloc(sizeof(TenEdge));
cin>>edge->info;edge->Out=G.TenDotArray[--cur1].OutNext;
G.TenDotArray[cur1].OutNext=edge; edge->DotOut=--cur2;
//以上两行处理“出 ”的相关
edge->DotIn=cur1;
edge->In=G.TenDotArray[cur2].InNext;
G.TenDotArray[cur2].InNext=edge;
//以上两行处理“入 ”的相关;
}
}
//打印出邻接矩阵
void PrintMatrixGraph(AdjoinMatrix G){
cout<<" "<<" ";
for(int i=0;i<G.Dotnum;i++){
cout<<G.Dot[i]<<" ";
}
cout<<endl;
for(int i=0;i<G.Dotnum;i++){
cout<<G.Dot[i]<<" ";
for(int j=0;j<G.Dotnum;j++){
cout<<G.Edge[i][j]<<" ";
}
cout<<endl;
}
}
//打印一个邻接表
void PrintAdjoinGraph(AdjoinChart G){
for(int i=0;i<G.Dotnum;i++){
Edge* cur=G.DotArray[i].FirNext;
while(cur){
cout<<G.DotArray[i].name<<"->"<<G.DotArray[cur->Dot].name<<"\t";
cur=cur->next;
}
cout<<endl;
}
}
//打印一个十字链表
void PrintTenAdjoinGraph(TenAdjoinChart G){
cout<<"正向打印十字链表"<<endl;
for(int i=0;i<G.TenDotnum;i++){
TenEdge* cur=G.TenDotArray[i].OutNext;
while(cur){
cout<<G.TenDotArray[i].name<<"->"<<G.TenDotArray[cur->DotOut].name<<"\t";
cur=cur->Out;
}
cout<<endl;
}
cout<<"逆向打印十字链表"<<endl;
for(int i=0;i<G.TenDotnum;i++){
TenEdge* cur=G.TenDotArray[i].InNext;
while(cur){
cout<<G.TenDotArray[cur->DotIn].name<<"->"<<G.TenDotArray[i].name<<"\t";
cur=cur->In;
}
cout<<endl;
}
}
//深度优先遍历(邻接矩阵)
//从i这个结点深度优先遍历
void DFSMatrix(AdjoinMatrix G,int i){
for(int j=0;j<G.Dotnum;j++){
if(G.Edge[i][j]!=0&&visit[j]==false){
visit[j]=true;
cout<<G.Dot[j]<<" ";
DFSMatrix(G,j);
}
}
}
void DFSAdjoinMatrix(AdjoinMatrix G){
cout<<"邻接矩阵深度优先遍历此时是";
for(int i=0;i<G.Dotnum;i++){
if(visit[i]==false){
visit[i]=true;
cout<<G.Dot[i]<<" ";
DFSMatrix(G,i);
}
}
cout<<endl;
}
//广度优先遍历(邻接矩阵)
//从i这个结点广度优先遍历
void BFSMatrix(AdjoinMatrix G,int i){
queue<int> Q;
Q.push(i);
while(!Q.empty()){
int cur=Q.front();
Q.pop();
for(int j=0;j<G.Dotnum;j++){
if(visit2[j]==false&&G.Edge[cur][j]!=0){
visit2[j]=true;
cout<<G.Dot[j]<<" ";
Q.push(j);
}
}
}
}
void BFSAdjoinMatrix(AdjoinMatrix G){
cout<<"邻接矩阵广度优先遍历此时是";
for(int i=0;i<G.Dotnum;i++){
if(visit2[i]==false){
visit2[i]=true;
cout<<G.Dot[i]<<" ";
BFSMatrix(G,i);
}
}
cout<<endl;
}
//深度优先遍历邻接表
void DFSChart(AdjoinChart G2,int i){
Edge* cur=G2.DotArray[i].FirNext;
while(cur){
if(visit[cur->Dot]==false){
visit[cur->Dot]=true;
cout<<G2.DotArray[cur->Dot].name<<" ";
DFSChart(G2,cur->Dot);
}
cur=cur->next;
}
}
void DFSAdjoinChart(AdjoinChart G2){
cout<<"邻接表深度优先遍历此时是";
for(int i=0;i<G2.Dotnum;i++){
if(visit[i]==false){
visit[i]=true;
cout<<G2.DotArray[i].name<<" ";
DFSChart(G2,i);
}
}
cout<<endl;
}
//广度优先遍历邻接表
void BFSChart(AdjoinChart G2,int i){
queue<int> Q;
Q.push(i);
while(!Q.empty()){
int cur=Q.front();
Q.pop();
for(Edge* j=G2.DotArray[cur].FirNext;j!=NULL;j=j->next){
if(visit2[j->Dot]==false){
visit2[j->Dot]=true;
cout<<G2.DotArray[j->Dot].name<<" ";
Q.push(j->Dot);
}
}
}
}
void BFSAdjoinChart(AdjoinChart G2){
cout<<"邻接表广度优先遍历此时是";
for(int i=0;i<G2.Dotnum;i++){
if(visit2[i]==false){
visit2[i]=true;
cout<<G2.DotArray[i].name<<" ";
BFSChart(G2,i);
}
}
cout<<endl;
}
//使用Prim(邻接矩阵)
void PrimAdjoinMatrix(AdjoinMatrix G){
//首先对dict数组进行初始化
int dict[G.Dotnum];
for(int i=0;i<G.Dotnum;i++)
dict[i]=IntMax;
for(int i=0;i<G.Dotnum;i++){
visit[i]=false;
if(G.Edge[0][i]) dict[i]=G.Edge[0][i];
}
dict[0]=0;visit[0]=true;
for(int k=1;k<G.Dotnum;k++){
//从dict中寻找一个最小的
int Min=IntMax; int LocMin=0;
for(int i=0;i<G.Dotnum;i++){
if(dict[i]<Min&&visit[i]==false){
Min=dict[i],LocMin=i;
}
}
//将最小的放入树中
visit[LocMin]=true;
//更行dict数组
for(int i=0;i<G.Dotnum;i++){
if(dict[i]>G.Edge[LocMin][i]&&G.Edge[LocMin][i]!=0){
dict[i]=G.Edge[LocMin][i];
}
}
}
cout<<"此时的dict数组是";
for(int i=0;i<G.Dotnum;i++){
cout<<dict[i]<<" ";
}
cout<<endl;
}
//***************使用克鲁斯卡尔来计算最小生成树 ***********
bool cmp(KEdge i,KEdge j){
return i.info<j.info;
}
int KlskerAdjoinMatrix(AdjoinMatrix G){
//使用一个数组给每一个结点命名,为了防止闭环
int name[G.Dotnum];
for(int i=0;i<G.Dotnum;i++){
name[i]=i;
}
//需要定义一个新的边结构,并完成此数组的初始化
KEdge edge[G.Edgenum];int k=0;
for(int i=0;i<G.Dotnum;i++){
for(int j=i;j<G.Dotnum;j++){
if(G.Edge[i][j]!=0){
edge[k].from=i;
edge[k].to=j;
edge[k++].info=G.Edge[i][j];
}
}
}
//将边按照从小到大排序
sort(edge,edge+G.Edgenum,cmp);
//从中选择边 ,但是需要判断是否是同一个树中的结点
int num=0;//记录此时选择边的个数
int sum=0;//记录此时最小树的最小值
for(int i=0;i<G.Edgenum;i++){
if(num==G.Dotnum-1) break;
if(name[edge[i].from]==name[edge[i].to]){
continue;
}
else{
sum+=edge[i].info;
int flag1=name[edge[i].from];
int flag2=name[edge[i].to];
for(int j=0;j<G.Dotnum;j++){
if(name[j]==flag2){
name[j]=flag1;
}
}
}
}
cout<<"通过克鲁斯卡尔计算的最小生成树的最小值是"<<sum<<endl;
return sum;
}
//*******使用克鲁斯卡尔来最小化邻接表
int KlskerAdjoinChart(AdjoinChart G2){
//使用一个数组给每一个结点命名,为了防止闭环
int name[G2.Dotnum];
for(int i=0;i<G2.Dotnum;i++){
name[i]=i;
}
//需要定义一个新的边结构,并完成此数组的初始化
KEdge edge[G2.Edgenum];int k=-1;
for(int i=0;i<G2.Dotnum;i++){
Edge* cur=G2.DotArray[i].FirNext;
while(cur){
edge[++k].from=i;
edge[k].to=cur->Dot;
edge[k].info=cur->info;
cur=cur->next;
}
}
//将边按照从小到大排序
sort(edge,edge+G2.Edgenum,cmp);
//从中选择边 ,但是需要判断是否是同一个树中的结点
int num=0;//记录此时选择边的个数
int sum=0;//记录此时最小树的最小值
for(int i=0;i<G2.Edgenum;i++){
if(num==G2.Dotnum-1) break;
if(name[edge[i].from]==name[edge[i].to]){
continue;
}
else{
sum+=edge[i].info;
int flag1=name[edge[i].from];
int flag2=name[edge[i].to];
for(int j=0;j<G2.Dotnum;j++){
if(name[j]==flag2){
name[j]=flag1;
}
}
}
}
cout<<"邻接表通过克鲁斯卡尔计算的最小生成树的最小值是"<<sum<<endl;
return sum;
}
/* 对邻接矩阵迪杰斯特拉算法算单源最短路径*/
void DjstlAdjoinMatrix(AdjoinMatrix G){
int dict[G.Dotnum];
//对dict数组进行初始化
for(int i=0;i<G.Dotnum;i++){
visit[i]=false;
if(G.Edge[0][i]){
dict[i]=G.Edge[0][i];
}
else{
dict[i]=IntMax;
}
}
//这里默认从第一个结点作为顶点,将第一个顶点置为0且访问过
dict[0]=0;visit[0]=true;
for(int k=1;k<G.Dotnum;k++){
//从dict数组中选择一个最小的
int Min=IntMax;int LocMin=0;
for(int i=0;i<G.Dotnum;i++){
if(dict[i]<Min){
Min=dict[i]; LocMin=i;
}
}
visit[LocMin]=true;
//基于这个最小值来更行dict数组
for(int i=0;i<G.Dotnum;i++){
if(visit[i]==false&&dict[i]>dict[k]+G.Edge[k][i]&&G.Edge[k][i]!=0){
dict[i]=dict[k]+G.Edge[k][i];
}
}
}
cout<<"通过迪杰斯特拉得到的dict数组是";
for(int i=0;i<G.Dotnum;i++){
cout<<dict[i]<<" ";
}
cout<<endl;
}
/*弗洛伊德来计算多源点之间的距离*/
void FloydAdjoinMatrix(AdjoinMatrix G){
int dict[G.Dotnum][G.Dotnum];
char Pre[G.Dotnum][G.Dotnum];
//对dict数组进行初始化
for(int i=0;i<G.Dotnum;i++){
for(int j=0;j<G.Dotnum;j++){
if(G.Edge[i][j]||i==j){
dict[i][j]=G.Edge[i][j];
Pre[i][j]=G.Dot[i];
}
else{
Pre[i][j]='N';
dict[i][j]=IntMax;
}
}
}
for(int k=0;k<G.Dotnum;k++){
for(int i=0;i<G.Dotnum;i++){
for(int j=0;j<G.Dotnum;j++){
if(dict[i][k]+dict[k][j]<dict[i][j]){
Pre[i][j]=G.Dot[k];
dict[i][j]=dict[i][k]+dict[k][j];
}
}
}
}
cout<<"通过弗洛伊德得到的dict[][]是"<<endl;
cout<<" "<<"\t";
for(int i=0;i<G.Dotnum;i++){
cout<<G.Dot[i]<<"\t";
}
cout<<endl;
for(int i=0;i<G.Dotnum;i++){
cout<<G.Dot[i]<<"\t";
for(int j=0;j<G.Dotnum;j++){
cout<<dict[i][j]<<"\t";
}
cout<<endl;
}
cout<<endl;
cout<<"通过弗洛伊德得到的Pre[][]是"<<endl;
cout<<" "<<"\t";
for(int i=0;i<G.Dotnum;i++){
cout<<G.Dot[i]<<"\t";
}
cout<<endl;
for(int i=0;i<G.Dotnum;i++){
cout<<G.Dot[i]<<"\t";
for(int j=0;j<G.Dotnum;j++){
cout<<Pre[i][j]<<"\t";
}
cout<<endl;
}
cout<<endl;
}
//使用拓扑排序对邻接表有向图进行排序
void TopologicalSortChart(AdjoinChart G){
//基本思想是先找一个出度为零的结点 然后删除结点,并且将此结点对印的弧也删
//这里我们使用一个栈将count为空的存储
int count[MaxDot]={0};
cout<<"此图经过拓扑排序之后的结果是"<<endl;
stack<int> S;
//首先统计每一个结点的出度 将入度为零的放入栈中
for(int i=0;i<G.Dotnum;i++){
Edge* cur=G.DotArray[i].FirNext;
while(cur){
count[cur->Dot]++;
cur=cur->next;
}
}
for(int i=0;i<G.Dotnum;i++){
if(count[i]==0) S.push(i);
}
int num=0;//记录弹出的次数
while(!S.empty()){
num++;
int cur=S.top();
S.pop();count[cur]=-1;
cout<<G.DotArray[cur].name<<" ";
//删除此弹出的相关的出弧
Edge* Ecur=G.DotArray[cur].FirNext;
while(Ecur){
if(--count[Ecur->Dot]==0){
S.push(Ecur->Dot);
}
Ecur=Ecur->next;
}
}
if(num<G.Dotnum){
cout<<"此时是一个带环图"<<endl;
}
return ;
}
int main(){
AdjoinMatrix G;AdjoinChart G2;TenAdjoinChart G3;
cout<<"请问你要选择以何种方式创建图"<<endl;
cout<<"1、邻接矩阵 2、邻接表 3、十字链表"<<endl;
int input;cin>>input;
switch(input){
case 1:{
CreatMatrixGraph(G);
PrintMatrixGraph(G);
DFSAdjoinMatrix(G);
BFSAdjoinMatrix(G);
PrimAdjoinMatrix(G);
KlskerAdjoinMatrix(G);
DjstlAdjoinMatrix(G);
FloydAdjoinMatrix(G);
// TopologicalSortMaxtrix(G);要在邻接矩阵中实现这个功能,不能对称存储 只需要邻接赋值的时候不要对称赋值便可
break;
}
case 2:{
CreatChartGraph(G2);
PrintAdjoinGraph(G2);
DFSAdjoinChart(G2);
BFSAdjoinChart(G2);
KlskerAdjoinChart(G2);
TopologicalSortChart(G2);
break;
}
case 3:{
CreatTenAdjoinGraph(G3);
PrintTenAdjoinGraph(G3);
break;
}
}
return 0;
}