加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

数据结构--基于天勤--有向有权图

发布时间:2020-12-15 04:49:31 所属栏目:百科 来源:网络整理
导读:/* 图(有向有权图) 2018.12.16 初步完成 2018.12.18 加入文件操作 */ #include #include #define MAXSIZE 100 #define INF 1000 int visit[MAXSIZE];//【深度/广度优先遍历使用】全局数组,存放元素访问标记,访问为1,未访问为0 int dist[MAXSIZE];//【单

/*

图(有向有权图)

2018.12.16 初步完成

2018.12.18 加入文件操作

*/

#include

#include

#define MAXSIZE 100

#define INF 1000

int visit[MAXSIZE];//【深度/广度优先遍历使用】全局数组,存放元素访问标记,访问为1,未访问为0

int dist[MAXSIZE];//【单源最短路径使用】从源结点到每个结点的最短路径的长度

int path[MAXSIZE];//【单源最短路径使用】保存path[vi]从v0到vi最短路径上vi的前一个结点

//【边--链表存储】定义 边 ------存储边的数据结构是 链表

typedef struct EdgeNode?

{

?? ?int adjacentVertex;//该边指向的结点位置(下一个节点),相当于指是记录指向的结点的下标 adjacent ?vertex 临接结点

?? ?int weight;//定义边的权值

?? ?struct EdgeNode *nextEdge;//指向下一条边的指针

}EdgeNode;

//【顶点--数组存储】定义 顶点 -------存储顶点的数据结构是 数组

typedef struct VertexNode

{

?? ?char data;//顶点信息

?? ?EdgeNode *firstEdge;//指向第一条边的指针

}VertexNode;

//定义 图

typedef struct

{

?? ?VertexNode adjacentList[MAXSIZE];//【邻接链表表头数组】[是一个结构体数组](每个元素都含有 顶点信息 和 指向第一条边的指针)

?? ?int vertexNumber;//顶点数

?? ?int edgeNumber;//边数

?? ?int edgeWeight[MAXSIZE][MAXSIZE];//【有向有权图-邻接矩阵】两顶点的权值(有向有权图),无法直接到达设为INF

}Graph;

//置0一个一维数组

void setZero(int *visit)

{

?? ?for (int i = 0; i < MAXSIZE; i++)

?? ?{

?? ??? ?visit[i] = 0;

?? ?}

?? ?return;

}

//链表建立方式为 头插法

void createGraphF(Graph *g)

{

?? ?//初始化图的 顶点数 边数

?? ?int vertexNumber;

?? ?int edgeNumber;

?? ?printf("请输入要建立的图的 顶点数 边数:");

?? ?scanf("%d %d",&vertexNumber,&edgeNumber);

?? ?g->vertexNumber = vertexNumber;

?? ?g->edgeNumber = edgeNumber; ? ? ?//图的顶点数和边数初始化完毕

?? ?//初始化图的 权重数组 全部赋值为INF

?? ?for (int i = 0; i < g->vertexNumber; i++)

?? ??? ?for (int j = 0; j < g->vertexNumber; j++)

?? ??? ?{

?? ??? ??? ?g->edgeWeight[i][j] = INF;

?? ??? ?}

?? ?

?? ?//建立了只有顶点的图

?? ?for(int i =0;i

?? ?{

?? ??? ?g->adjacentList[i].data = i;

?? ??? ?g->adjacentList[i].firstEdge = NULL;

?? ?}

?? ?EdgeNode *newNode;//用来临时存放边结点

?? ?//插入边--边的开始 结束/边的权值

?? ?for (int i = 0; i < g->edgeNumber; i++)

?? ?{

?? ??? ?//插入边和权值

?? ??? ?int startSubscript;//前驱结点的下标

?? ??? ?int endSubscript;//后继结点的下表

?? ??? ?int weight;//边的权重

?? ??? ?scanf("%d %d %d",&startSubscript,&endSubscript,&weight);

?? ??? ?g->edgeWeight[startSubscript][endSubscript] = weight;//一边输入,一遍把图的 权重二维数组建立好

?? ??? ?newNode = (EdgeNode*)malloc(sizeof(EdgeNode));//申请边的空间

?? ??? ?newNode->adjacentVertex = endSubscript;//边的后继结点

?? ??? ?newNode->weight = weight;//边的权值

?? ??? ?newNode->nextEdge = g->adjacentList[startSubscript].firstEdge;//新结点指向 表头结点的下一结点(是一条边NULL)

?? ??? ?g->adjacentList[startSubscript].firstEdge = newNode; //表头结点的下一结点 指向 新结点

?? ?}

}

//链表建立方式为 尾插法

void createGraphR(Graph *g)

{

?? ?//初始化图的 顶点数 边数

?? ?int vertexNumber;

?? ?int edgeNumber;

?? ?printf("请输入要建立的图的 顶点数 边数:");

?? ?//直接通过文件输入数据

?? ?FILE *fin;

?? ?fin = fopen("input.txt","r");

?? ?fscanf(fin,"%d %d",&edgeNumber);

?? ?//scanf("%d %d",&edgeNumber);

?? ?g->vertexNumber = vertexNumber;

?? ?g->edgeNumber = edgeNumber; ? ? ?//图的顶点数和边数初始化完毕

?? ?//初始化图的 权重数组 全部赋值为INF

?? ?for(int i =0;ivertexNumber;i++)

?? ??? ?for (int j = 0; j < g->vertexNumber; j++)

?? ??? ?{

?? ??? ??? ?g->edgeWeight[i][j] = INF;

?? ??? ?}

?? ?//建立了只有顶点的图

?? ?for (int i = 0; i < vertexNumber; i++)

?? ?{

?? ??? ?g->adjacentList[i].data = i;

?? ??? ?g->adjacentList[i].firstEdge = NULL;

?? ?}

?? ?EdgeNode *newNode;//用来临时存放边结点

?? ? ? ? ? ? ? ? ? ? ?//插入边--边的开始 结束/边的权值

? ? //边结点temp直接定位到第一个边结点,用来辅助进行尾插法

?? ?EdgeNode *temp;

?? ?//temp = (EdgeNode*)malloc(sizeof(EdgeNode));

?? ?for (int i = 0; i < g->edgeNumber; i++)

?? ?{

?? ??? ?//插入边和权值

?? ??? ?int startSubscript;//前驱结点的下标

?? ??? ?int endSubscript;//后继结点的下表

?? ??? ?int weight;//边的权重

?? ??? ?//scanf("%d %d %d",&weight);

?? ??? ?fscanf(fin,"%d %d %d",&weight);

?? ??? ?g->edgeWeight[startSubscript][endSubscript] = weight;//一边输入,一遍把图的 权重二维数组建立好

?? ??? ?newNode = (EdgeNode*)malloc(sizeof(EdgeNode));//申请边的空间

?? ??? ?newNode->nextEdge = NULL;//因为要进行尾插法,所以newNode作为最后一个结点,其next为NULL

?? ??? ?newNode->adjacentVertex = endSubscript;//边的后继结点

?? ??? ?newNode->weight = weight;//边的权值

?? ??? ?//如果链表只有表头结点,直接将 newNode接在表头结点(第一个结点)后面

?? ??? ?if (g->adjacentList[startSubscript].firstEdge == NULL)

?? ??? ?{

?? ??? ??? ?newNode->nextEdge = g->adjacentList[startSubscript].firstEdge;//新结点指向 表头结点的下一结点(是一条边NULL)

?? ??? ??? ?g->adjacentList[startSubscript].firstEdge = newNode; //表头结点的下一结点 指向 新结点

?? ??? ?}

?? ??? ?//如果链表既有表头结点,又有边结点,那么直接定位到边结点,然后进行循环判空,找到 最后一个边结点

?? ??? ?//然后在最后一个边结点后面插入 newNode

?? ??? ?else //if(g->adjacentList[startSubscript].firstEdge != NULL)

?? ??? ?{

?? ??? ??? ?temp = g->adjacentList[startSubscript].firstEdge;//将第一个边结点 赋给 temp,由temp辅助尾插法进行操作

?? ??? ??? ?while (temp->nextEdge != NULL)//temp不是最后一个边结点,那么temp往后移动

?? ??? ??? ?{

?? ??? ??? ??? ?temp = temp->nextEdge;

?? ??? ??? ?}

?? ??? ??? ?//接下来,如果temp是最后一个结点,那么将newNode作为 temp->nextEdge,及将newNode作为最后的边结点

?? ??? ??? ?temp->nextEdge = newNode;

?? ??? ?}

?? ?

?? ?}

}

//以邻接链表的方式打印图

void printGraph(Graph g,FILE* fout)

{

?? ?printf("图的邻接链表表示为:");

?? ?int i;

?? ?EdgeNode *p;

?? ?for (i = 0; i < g.vertexNumber; i++) {

?? ??? ?printf("n%d",g.adjacentList[i].data);//打印表头结点

?? ??? ?fprintf(fout,"n%d",g.adjacentList[i].data);//打印表头结点

?? ??? ?p = g.adjacentList[i].firstEdge;//p现在是第一条边

?? ??? ?while (p != NULL) {

?? ??? ??? ?printf("-->%d",p->adjacentVertex); //打印第一条边指向的顶点

?? ??? ??? ?fprintf(fout,"-->%d",p->adjacentVertex); //打印第一条边指向的顶点

?? ??? ??? ?p = p->nextEdge;//之后p到下一条边

?? ??? ?}

?? ?}

?? ?return;

?? ?printf("n");

}

//以邻接表的方式打印图

void printWeight(Graph g,FILE* fout)

{

?? ?printf("图的邻接矩阵为:n");

?? ?fprintf(fout,"图的邻接矩阵为:n");

?? ?for(int i =0;i

?? ?{

?? ??? ?for (int j = 0; j < g.vertexNumber; j++)

?? ??? ?{

?? ??? ??? ?printf("%4d ? ? ? ?",g.edgeWeight[i][j]);

?? ??? ??? ?fprintf(fout,"%4d ? ? ? ?",g.edgeWeight[i][j]);

?? ??? ?}

?? ??? ?printf("n");

?? ??? ?fprintf(fout,"n");

?? ?}

}

//深度优先遍历 ?

void dfs(Graph *g,int vertex,FILE* fout)

{

?? ?//【开始】遍历 从顶点vertex出发的连通子图

?? ?EdgeNode *p;

?? ?visit[vertex] = 1;

?? ?printf("V%d ",vertex); //遍历操作

?? ?fprintf(fout,"V%d ",vertex); //遍历操作

?? ?p = g->adjacentList[vertex].firstEdge;//p指向表头结点指向的第一条边

?? ?while (p != NULL)

?? ?{

?? ??? ?if (visit[p->adjacentVertex] == 0)

?? ??? ??? ?dfs(g,p->adjacentVertex,fout);//因为函数定义 g为Graph类型的指针,所以在接下来的递归中语句不会出错

?? ??? ?p = p->nextEdge;

?? ?}

?? ?//【结束】遍历 从顶点vertex出发的连通子图

?? ?//【开始】遍历其他 子图

?? ?for (int i = 0; i < g->vertexNumber; i++) //

?? ?{

?? ??? ?if (visit[i] == 0)

?? ??? ?{

?? ??? ??? ?dfs(g,i,fout);//因为函数定义 g为Graph类型的指针,所以在接下来的递归中语句不会出错

?? ??? ?}

?? ?}

?? ?//【结束】遍历其他 子图

}

//广度优先遍历

void bfs(Graph *g,FILE* fout)

{

?? ?EdgeNode *p;

?? ?int queue[MAXSIZE],front = 0,rear = 0;//队列的便捷定义方式

?? ?visit[vertex] = 1; //全局数组标记为1:表示已经访问过

?? ?printf("V%d ",vertex);//【遍历操作】访问过后打印输出

?? ?fprintf(fout,vertex);//【遍历操作】访问过后打印输出

?? ?rear = (rear + 1) % MAXSIZE;//入队操作

?? ?queue[rear] = vertex;

?? ?int temp;

?? ?while (front != rear)//当队列空的时候说明遍历完成

?? ?{

?? ??? ?front = (front + 1) % MAXSIZE;//顶点出队

?? ??? ?temp = queue[front];

?? ??? ?p = g->adjacentList[temp].firstEdge;//将p指向出队顶点的第一条边

?? ??? ?while (p != NULL)

?? ??? ?{

?? ??? ??? ?if (visit[p->adjacentVertex] == 0)//如果p的邻接结点未被访问,则入队

?? ??? ??? ?{

?? ??? ??? ??? ?visit[p->adjacentVertex] = 1;

?? ??? ??? ??? ?printf("V%d ",p->adjacentVertex);

?? ??? ??? ??? ?fprintf(fout,p->adjacentVertex);

?? ??? ??? ??? ?rear = (rear + 1) % MAXSIZE;//入队操作

?? ??? ??? ??? ?queue[rear] = p->adjacentVertex;

?? ??? ??? ?}

?? ??? ??? ?p = p->nextEdge;

?? ??? ?}

?? ?}

?? ?//【开始】遍历其他 子图

?? ?for (int i = 0; i < g->vertexNumber; i++) //

?? ?{

?? ??? ?if (visit[i] == 0)

?? ??? ?{

?? ??? ??? ?bfs(g,fout);//因为函数定义 g为Graph类型的指针,所以在接下来的递归中语句不会出错

?? ??? ?}

?? ?}

?? ?//【结束】遍历其他 子图

}

//dijkstra算法-有向有权图-单源最短路径算法

void dijkstra(Graph g,int v,int *dist,int *path)

{

?? ?int set[MAXSIZE];//顶点已进入最短路径标志位

?? ?int min,j,u;

?? ?//初始化算法所用的数组

?? ?for (i = 0; i < g.vertexNumber; i++)

?? ?{

?? ??? ?dist[i] = g.edgeWeight[v][i];

?? ??? ?set[i] = 0;

?? ??? ?if (g.edgeWeight[v][i] < INF)

?? ??? ??? ?path[i] = v;

?? ??? ?else

?? ??? ??? ?path[i] = -1;

?? ?}

?? ?set[v] = 1;

?? ?path[v] = -1;

?? ?//关键算法

?? ?for (i = 0; i < g.vertexNumber; i++)

?? ?{

?? ??? ?//从不在最短路径的顶点中,找出dist[]最短的,加入最短路径

?? ??? ?min = INF;

?? ??? ?for(j=0;j

?? ??? ??? ?if (set[j] == 0 && dist[j] < min)

?? ??? ??? ?{

?? ??? ??? ??? ?u = j;

?? ??? ??? ??? ?min = dist[j];

?? ??? ??? ?}

?? ??? ?set[u] = 1;

?? ??? ?//更新dist[]

?? ??? ?for (j = 0; j < g.vertexNumber; j++)

?? ??? ?{

?? ??? ??? ?if (set[j] == 0 && dist[u]+g.edgeWeight[u][j]

?? ??? ??? ?{

?? ??? ??? ??? ?dist[j] = dist[u] + g.edgeWeight[u][j];

?? ??? ??? ??? ?path[j] = u;

?? ??? ??? ?}

?? ??? ?}

?? ?}

}

//需要创建两个文件 input.txt output.txt

//数据内容输入文件 input.txt

/*

6 ?11

0 ?1 ?50

0 ?2 ?10

0 ?4 ?45

1 ?2 ?15

1 ?4 ?10

2 ?0 ?20

2 ?3 ?15

3 ?1 ?20

3 ?4 ?35

4 ?3 ?30

5 ?3 ?3

*/

int main()

{

?? ?

?? ?Graph g;

?? ?//createGraphF(&g);

?? ?createGraphR(&g);

?? ?FILE* fout;

?? ?fout = fopen("output.txt","w");

?? ?printGraph(g,fout);

?? ?printf("nn"); fprintf(fout,"nn");

?? ?printWeight(g,"nn");

?? ?printf("从顶点1开始的深度优先遍历:");

?? ?fprintf(fout,"从顶点1开始的深度优先遍历:");

?? ?setZero(visit);

?? ?dfs(&g,1,"nn");

?? ?

?? ?printf("从顶点1开始的广度优先遍历:");

?? ?fprintf(fout,"从顶点1开始的广度优先遍历:");

?? ?setZero(visit);

?? ?bfs(&g,"nn");

?? ?printf("顶点0到顶点的最短路径:n");

?? ?fprintf(fout,"顶点0到顶点的最短路径:n");

?? ?int start = 0;

?? ?dijkstra(g,start,dist,path);

?? ?for (int i = 0; i < g.vertexNumber; i++)

?? ?{

?? ??? ?if (i == start)continue;

?? ??? ?printf("V%d->V%d:%dn",dist[i]);

?? ??? ?fprintf(fout,"V%d->V%d:%dn",dist[i]);

?? ?}

?? ?return 0;

}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读