数据结构与算法-图的操作及应用C语言实现(超详细注释/设计/实验/作业)

一、引言

感谢宝子们一键三连支持,火速更新中~~
本文介绍了图的创建,深度优先遍历,广度优先遍历,普利姆算法最小生成树,克鲁斯卡尔算法最小生成树,迪杰斯特拉最短路径,弗洛伊德算法最短路径。

二、目的

  1. 掌握图的存储结构
  2. 掌握图的基本操作
  3. 掌握图的简单应用

三、环境与设备

操作系统:Windows 10
编译器:Visual Studio 2021

四、存储结构

typedef int DataType;
typedef int VRType;
typedef enum{DN,DG,UDN,UDG}GraphKind;       //枚举图的 4 种类型
//****************************边表结点****************************
typedef struct node
{
	int adjvex;/*存储与之联系的顶点下标*/
	VRType weight;/*若为无权图,则无该部分,若为有权图,则存储权值*/ 
	struct node *nextarc;/*下一条边*/
}ArcNode;
//****************************顶点表结点****************************
typedef struct
{
	DataType vex;
	ArcNode *firstArc;/*与当前顶点有联系的第一条边或者弧*/ 
}VertexNode;
//****************************邻接表结构****************************
typedef struct
{
	VertexNode adjList[MAX];/*顶点表结点数组*/
	int vexnum;/*图的实际顶点数*/
	int arcnum;/*边数或弧数*/
	GraphKind kind;/*图的类型*/ 
}ALGraph;

五、函数

(1)	void CreateAL(ALGraph *G,FILE *fp)//创建图 
(2)	void ALDepthFirstSearch(ALGraph *G)//图的深度优先遍历
(3)	void ALBreadthFirstSearch(ALGraph *G) //图的广度优先遍历
(4)	void MiniTree(ALGraph *G) //普利姆算法最小生成树
(5)	void kruskal(ALGraph *G) //克鲁斯卡尔算法最小生成树
(6)	void MiniDis(ALGraph *G,int sVex) //迪杰斯特拉最短路径
(7)	void Floyd(ALGraph *G) //弗洛伊德算法最短路径

六、核心代码

 #include<stdio.h>
#include<stdlib.h>
#include<limits.h>
#define MAX 100
typedef int DataType;
typedef int VRType;
typedef enum{DN,DG,UDN,UDG}GraphKind;

//枚举图的4种类型
 
//****************************边表结点****************************

typedef struct node
{
int adjvex;/*存储与之联系的顶点下标*/
VRType weight;/*若为无权图,则无该部分,若为有权图,则存储权值*/ struct node *nextarc;/*下一条边*/
}ArcNode;

//****************************顶点表结点****************************

typedef struct
{
DataType vex;
ArcNode *firstArc;/*与当前顶点有联系的第一条边或者弧*/ }VertexNode;

//****************************邻接表结构****************************

typedef struct

{

VertexNode adjList[MAX];/*顶点表结点数组*/

int vexnum;/*图的实际顶点数*/

int arcnum;/*边数或弧数*/

GraphKind kind;/*图的类型*/

}ALGraph;

//****************************创建图****************************

void CreateAL(ALGraph *G,FILE *fp)

{

int i,j,temp;
ArcNode *arc;
//printf("请输入图的顶点数:");
fscanf(fp,"%d",&G->vexnum);
//printf("请输入图的种类:");
fscanf(fp,"%d",&G->kind);
for(i=0;i<G->vexnum;i++)
fscanf(fp,"%d",&G->adjList[i].vex); //printf("请输入顶点信息:"); G->adjList[i].firstArc=NULL;
}
G->arcnum=0;
//printf("请输入邻接表:\n");
for(i=0;i<G->vexnum;i++)
{
for(j=0;j<G->vexnum;j++)
{
fscanf(fp,"%d",&temp);
if(temp)
{
arc=malloc(sizeof(ArcNode));

arc->adjvex=j;

arc->weight=temp;

arc->nextarc=G->adjList[i].firstArc;

G->adjList[i].firstArc=arc;

G->arcnum++;

}

}

}

if(G->kind==0 || G->kind==2)

G->arcnum/=2;

fclose(fp);

}

//****************************输出图****************************

void OutputAL(ALGraph *G)

{

int i,j;

ArcNode *arc;

int temp[MAX];

printf("\n 图的顶点数:%d\n",G->vexnum);

printf("图的边数:%d\n",G->arcnum);

printf("图的种类:%d\n",G->kind);

printf("顶点编号:\n");

for(i=0;i<G->vexnum;i++)

{

printf("V%d\t",G->adjList[i].vex);

}

printf("\n 邻接矩阵如下:\n");

for(i=0;i<G->vexnum;i++)

{

for(j=0;j<G->vexnum;j++)temp[j]=0;
 

arc=G->adjList[i].firstArc;

while(arc!=NULL)

{

temp[arc->adjvex]=arc->weight;

arc=arc->nextarc;

}

for(j=0;j<G->vexnum;j++)

printf("%d\t",temp[j]);

printf("\n");

}

printf("\n\n");

}

//****************************非递归深度优先遍历****************************

int visited[MAX];//存储结点是否访问过

void ALDepthFirstSearch1(ALGraph *G)

{

int i,j;

ArcNode *arc;

int num=0;

for(i=0;i<G->vexnum;i++)

{

visited[i]=0;//初始化所有结点都未访问过

}

i=0;num=0;

while(num<G->vexnum)

{

if(!visited[i])

{

printf("v%d ",G->adjList[i].vex);

visited[i]=1;

num++;

}

arc=G->adjList[i].firstArc;

while(arc!=NULL && visited[arc->adjvex])//跳过已经访问过的表结点

{

arc=arc->nextarc;

}

if(arc==NULL)

{

for(j=0;j<G->vexnum;j++)//下一个没被访问的顶点开始

{

if(visited[j]==0)

{
 

i=j;

break;

}

}

}

else

i=arc->adjvex;

}

printf("\n");

}

//****************************递归深度优先遍历**************************** void DepthFirstSearch(ALGraph *G,DataType i) {

ArcNode *arc;

printf("v%d ",G->adjList[i].vex);

visited[i]=1;

arc=G->adjList[i].firstArc;

while(arc!=NULL)

{

if(!visited[arc->adjvex])

DepthFirstSearch(G,arc->adjvex);

arc=arc->nextarc;

}

}

void ALDepthFirstSearch(ALGraph *G)

{

int visited[MAX];//存储结点是否访问过

int num=0;

for(int i=0;i<G->vexnum;i++)

{

visited[i]=0;//初始化所有结点都未访问过

}

for(int i=0;i<G->vexnum;i++)

{

if(!visited[i])

DepthFirstSearch(G,i);

}

}

//****************************广度优先遍历**************************** void ALBreadthFirstSearch(ALGraph *G)
 

{

int i,j;

ArcNode *arc;

int visited[MAX];//存储结点是否访问过

int s[MAX],front=0,rear=0;

for(i=0;i<G->vexnum;i++)

{

visited[i]=0;//初始化所有结点都未访问过

}

i=0;

s[rear]=i;rear=(rear+1)%MAX;

visited[i]=1;

while(front!=rear)

{

i=s[front];//出队列

front=(front+1)%MAX;

printf("v%d ",G->adjList[i].vex);

arc=G->adjList[i].firstArc;

while(arc!=NULL)

{

if(!visited[arc->adjvex])//未访问的邻接点入队

{

s[rear]=arc->adjvex;

rear=(rear+1)%MAX;

visited[arc->adjvex]=1;

}

arc=arc->nextarc;

}

if(front==rear)

{

for(j=0;j<G->vexnum;j++)//如果有顶点没访问过则入队

{

if(!visited[j])

{

s[rear]=j;

rear=(rear+1)%MAX;

visited[j]=1;

}

}

}

}

printf("\n");

}

//****************************	普	里	姆	算	法	求	最	小	生	成	树
 

****************************

void MiniTree(ALGraph *G)

{

int i,j,k;

ArcNode *arc;

int visited[MAX];

//	记录 U 到 V-U 具有代价最小的边 struct

{

int minlen; DataType sVex;//U

}Len[MAX];//若干条边中记录当前结点到 U 中的代价最小的边 struct

{

DataType sVex;//起点 DataType eVex;//终点

}list[MAX];//已选边

int num,min,loc;//顶点数、最小权值、权值最小的顶点 for(j=0;j<G->vexnum;j++)

{

Len[j].minlen=0;//到各点的权值初始化为 0 visited[j]=0;//初始化所有结点都未访问过

}

i=0;

visited[i]=1;

num=0;

list[num].sVex=i;

list[num].eVex=i;

num++; while(num<G->vexnum)

{

arc=G->adjList[i].firstArc;

while(arc!=NULL) //求 i 的所有邻接边中权值最小的

{

j=arc->adjvex; if(arc->weight!=0 && visited[j]!=1)

{

if(Len[j].minlen==0 || arc->weight<Len[j].minlen)

{

Len[j].minlen=arc->weight;

Len[j].sVex=i;

}

}

arc=arc->nextarc;
 

}

min=INT_MAX;

for(j=0;j<G->vexnum;j++) //求所有待选边中权值最小的

{

if(Len[j].minlen!=0 && visited[j]!=1 && min>Len[j].minlen)

{

min=Len[j].minlen;

loc=j;

}

}

list[num].sVex=Len[loc].sVex;//新添边的起点为权值最小的边的起点

list[num].eVex=loc;

visited[loc]=1;//标记权值最小的边的终点已访问

i=list[num].eVex;//已刚选的顶点作为起点,即顶点 i 并入 U

num++;

}

printf("普里姆算法,最小生成树序列为:\n");

for(j=1;j<G->vexnum;j++)

{

printf("Edge:v%d->v%d\n",G->adjList[list[j].sVex].vex,G->adjList[list[j].eVex]);

}

}

//****************************	克	鲁	斯	卡	尔	算	法	求	最	小	生	成	树

****************************

void kruskal(ALGraph *G)

{

int i,j,num,min,row,col,tag;

int visited[MAX];

ArcNode *arc,*temp;

struct

{

DataType sVex;

DataType eVex;

}MiniTree[MAX];

int s[MAX],front,rear;//队列

num=0;//边的数目

while(num<G->vexnum-1)

{

min=INT_MAX;

for(i=0;i<G->vexnum;i++)

{

arc=G->adjList[i].firstArc;

while(arc)//求每一个顶点权值最小的邻接点

{
 

j=arc->adjvex;

if(arc->weight!=0 && arc->weight<min)

{

min=arc->weight;

row=i;

col=j;

temp=arc;

}

arc=arc->nextarc;

}

}

temp->weight=0;

MiniTree[num].sVex=row;

MiniTree[num].eVex=col;

num++;//新增一条边,但可能无效,因为可能构成环路

//以下算法判断是否构成环路

for(i=0;i<num-1;i++)

visited[i]=0;

front=rear=0;

tag=1;

while(tag)

{

for(i=0;i<num-1;i++)

{

if(MiniTree[i].sVex==col)

{

s[rear]=MiniTree[i].eVex;

rear=(rear+1)%MAX;

visited[i]=1;

}

else if(MiniTree[i].eVex==col)

{

s[rear]=MiniTree[i].sVex;

rear=(rear+1)%MAX;

visited[i]=1;

}

}

while(front!=rear)

{

col=s[front];

front=(front+1)%MAX;

if(col==row)

{

tag=0;
 

num--;

break;

}

for(i=0;i<num-1;i++)

{

if(visited[i]==0 && MiniTree[i].sVex==col)

{

s[rear]=MiniTree[i].eVex;

rear=(rear+1)%MAX;

visited[i]=1;

}

else if(visited[i]==0 && MiniTree[i].eVex==col)

{

s[rear]=MiniTree[i].sVex;

rear=(rear+1)%MAX;

visited[i]=1;

}

}

}

if(front==rear)break;

}

}

printf("\n 克鲁斯卡尔算法,最小生成树序列为:\n");

for(i=0;i<num;i++)

printf("v%d->v%d\n",G->adjList[MiniTree[i].sVex].vex,G->adjList[MiniTree[i].eVex].vex);

}

//****************************	迪	杰	斯	特	拉	算	法	求	最	短	路	径

****************************

void MiniDis(ALGraph *G,int sVex)

{//sVex 是起点的存储下标

struct

{

int dis;//最短路径值

DataType path[MAX];//路径序列

int top;//顶点数目

}minipath[MAX];

int s[MAX],front=0,rear=0;

int i,j,temp;

ArcNode *arc;

for(i=0;i<G->vexnum;i++)

{

minipath[i].dis=0;//赋值为 0 表示走不通

minipath[i].top=1;

minipath[i].path[minipath[i].top-1]=sVex;
 

}

s[rear]=sVex;rear=(rear+1)%MAX;

while(front!=rear)

{

i=s[front];front=(front+1)%MAX;

arc=G->adjList[i].firstArc;

while(arc!=NULL)

{

j=arc->adjvex;

temp=arc->weight+minipath[i].dis;

if(minipath[j].dis!=0 && arc->adjvex!=sVex && temp<minipath[j].dis)

{

minipath[j]=minipath[i];

minipath[j].dis=temp;

minipath[j].path[minipath[j].top]=j;

minipath[j].top++;

s[rear]=j;rear=(rear+1)%MAX;

}

else if(minipath[j].dis == 0 && arc->adjvex!=sVex)

{

minipath[j]=minipath[i];

minipath[j].dis=temp;

minipath[j].path[minipath[j].top]=j;

minipath[j].top++;

s[rear]=j;rear=(rear+1)%MAX;

}

arc=arc->nextarc;

}

}

printf("\n 最短路径如下:\n");

for(i=0;i<G->vexnum;i++)

{

if(i!=sVex)

{

if(minipath[i].top>1)

{

printf("v%d->",sVex);

for(j=1;j<minipath[i].top-1;j++)

{

printf("v%d->",minipath[i].path[j]);

}

printf("v%d,dis:%d",minipath[i].path[j],minipath[i].dis);

}

else
 

printf("v%d 到 v%d 不通",sVex,i);

printf("\n");

}

}

}

//****************************	弗	洛	伊	德	算	法	求	最	短	路	径

****************************

void Floyd(ALGraph *G)

{

int PathMatrix[MAX][MAX];

int DistanceMatrix[MAX][MAX];

int v, w, k;

ArcNode *arc;

int temp[MAX];

//初始化 floyd 算法的两个矩阵

for(v=0;v<G->vexnum;v++)

{

arc=G->adjList[v].firstArc;

for(w=G->vexnum-1;w>=0;w--)

{

if(arc!=NULL && arc->adjvex==w)

{

DistanceMatrix[v][w] = arc->weight;

arc=arc->nextarc;

}

else

{

DistanceMatrix[v][w] = 1000;//赋值不能超过最大值的一半

}

PathMatrix[v][w] = w;

}

}

//k 为中间点

for(k=0;k<G->vexnum;k++)

{

//v 为起点

for(v=0;v<G->vexnum;v++)

{

//w 为终点

for(w=0;w<G->vexnum;w++)

{

if(DistanceMatrix[v][w] > (DistanceMatrix[v][k] + DistanceMatrix[k][w])){

DistanceMatrix[v][w] = DistanceMatrix[v][k] + DistanceMatrix[k][w];//更

新最小路径
 

PathMatrix[v][w] = PathMatrix[v][k];//更新最小路径中间顶点

}

}

}

}

v = 0;

w = 5;

//求 0 到 3 的最小路径

printf("\nv%d->v%d 的最小路径为:%d\n", v, w, DistanceMatrix[v][w]); k = PathMatrix[v][w];

printf("path: v%d", v);//打印起点

while(k != w){

printf("->v%d", k);//打印中间点

k = PathMatrix[k][w];

}

printf("->v%d\n", w);

}

int TopologicalSort(ALGraph *G,int *topo)

{

int i, m, k,j;

int s[MAX],top;

ArcNode *arc;

int indegree[MAX];

for(i=0;i<G->vexnum;i++)

{

indegree[i]=0;

for(j=0;j<G->vexnum;j++)

{

arc=G->adjList[j].firstArc;

while(arc!=NULL)

{

if(arc->adjvex==i)

indegree[i]++;

arc=arc->nextarc;

}

}

}

top=0;

for (i = 0; i < G->vexnum; i++)

{

if(!indegree[i])

{

s[top]=i;

top++;
 

}

}

m = 0;

while(top>0)

{

top--;

i=s[top];

topo[m] = i;

++m;

arc=G->adjList[i].firstArc;

while (arc != NULL)

{

k	= arc->adjvex; --indegree[k];

if (indegree[k] == 0)

{

s[top]=k;

top++;

}

arc = arc->nextarc;

}

}

topo[m] = -1;

if (m < G->vexnum)

{

printf("ERROR!\n");

return 0;

}

printf("\n 拓扑排序序列为:\n");

for (i = 0; topo[i] != -1; i++)

{

printf("v%d ", G->adjList[topo[i]].vex);

}

return 1;

}

//求各顶点的最晚发生时间并计算出各边的最早和最晚开始时间

int CriticalPath(ALGraph *G)

{

int i, j, k, e, l;

int *ve, *vl;

int topo[MAX];

ArcNode *p;

ve = (int *)malloc(sizeof(int)*G->vexnum);
 

vl = (int *)malloc(sizeof(int)*G->vexnum);

if (!TopologicalSort(G, topo))

return 0;

for (i = 0; i < G->vexnum; i++)

ve[i] = 0;

for (i = 0; i < G->vexnum; i++)

{

k = topo[i];

p	= G->adjList[k].firstArc; while (p)

{

j = p->adjvex;

if (ve[j] < ve[k] + p->weight) ve[j] = ve[k] + p->weight;

p = p->nextarc;

}

}

for (i = 0; i < G->vexnum; i++)

vl[i] = ve[G->vexnum - 1];

for (i = G->vexnum - 1; i >= 0; i--)

{

k = topo[i];

p	= G->adjList[k].firstArc; while (p)

{

j = p->adjvex;

if (vl[k]>vl[j] - p->weight) vl[k] = vl[j] - p->weight;

p = p->nextarc;

}

}

printf("\n");

for (i = 0; i < G->vexnum; i++)

{

p	= G->adjList[i].firstArc; while (p)

{

j = p->adjvex; e = ve[i];

l = vl[j] - p->weight; if (e == l)

printf("v%d->v%d\n", G->adjList[i].vex, G->adjList[j].vex); p = p->nextarc;

}
 

}

return 1;

}

void main()

{

ALGraph G;

FILE *fp;

int opt;

int topo[MAX];

printf("请选择相应操作:\n1:深度优先\n2:广度优先\n3:普利姆算法最小生成树\n4:克鲁

斯卡尔算法最小生成树\n5:迪杰斯特拉最短路径\n6:弗洛伊德算法最短路径\n7:拓扑排序\n8:

关键路径\n9:显示\n0:结束\n");

scanf("%d",&opt);

while(opt)

{

switch(opt)

{

case 1:

fp=fopen("t1234.txt","r");

CreateAL(&G,fp);

//OutputAL(&G);

printf("深度优先序列:\n");

ALDepthFirstSearch(&G);

break;

case 2:

fp=fopen("t1234.txt","r");

CreateAL(&G,fp);

printf("深度优先序列:\n");

ALBreadthFirstSearch(&G);

break;

case 3:

fp=fopen("t1234.txt","r");

CreateAL(&G,fp);

MiniTree(&G);

break;

case 4:

fp=fopen("t1234.txt","r");

CreateAL(&G,fp);

kruskal(&G);

break;

case 5:

fp=fopen("MiniDis.txt","r");

CreateAL(&G,fp);

MiniDis(&G,0);
 

break;

case 6:

fp=fopen("MiniDis.txt","r");

CreateAL(&G,fp);

Floyd(&G);

break;

case 7:

fp=fopen("CriticalPath.txt","r");

CreateAL(&G,fp);

TopologicalSort(&G,topo);

break;

case 8:

fp=fopen("CriticalPath.txt","r");

CreateAL(&G,fp);

CriticalPath(&G);

break;

case 9:

OutputAL(&G);

break;

default:

printf("输出错误!");

}

printf("请选择相应操作:\n1:深度优先\n2:广度优先\n3:普利姆算法最小生成树\n4:

克鲁斯卡尔算法最小生成树\n5:迪杰斯特拉最短路径\n6:弗洛伊德算法最短路径\n7:拓扑排

序\n8:关键路径\n9:显示\n0:结束\n");

scanf("%d",&opt);

}

}

七、调试界面

在这里插入图片描述

八、总结

好好学习,天天向上。本章较难理解,宝子们好好练习。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
很抱歉,我作为AI语言模型无法提供完整的数据结构C语言源代码及详细注释。但我可以为您提供一些数据结构C语言的基本概念和相关学习资源。 数据结构是计算机科学中的一个重要概念,它指的是一组数据的组织方式、存储方式以及操作方式。数据结构可以分为线性结构和非线性结构。常见的线性结构有数组、链表、栈和队列等,非线性结构有树、等。 在C语言中,可以使用结构体和指针来实现各种数据结构。例如,链表可以使用结构体和指针实现,代码如下: ``` //定义链表节点结构体 struct ListNode { int val; struct ListNode* next; }; //创建链表 struct ListNode* createList(int* nums, int numsSize) { struct ListNode* head = NULL; struct ListNode* tail = NULL; for (int i = 0; i < numsSize; i++) { struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); node->val = nums[i]; node->next = NULL; if (head == NULL) { head = node; tail = node; } else { tail->next = node; tail = node; } } return head; } //遍历链表 void traverseList(struct ListNode* head) { struct ListNode* p = head; while (p != NULL) { printf("%d ", p->val); p = p->next; } printf("\n"); } //插入节点到链表 struct ListNode* insertList(struct ListNode* head, int val) { struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); node->val = val; node->next = NULL; if (head == NULL) { head = node; } else { struct ListNode* p = head; while (p->next != NULL) { p = p->next; } p->next = node; } return head; } //删除链表节点 struct ListNode* deleteList(struct ListNode* head, int val) { if (head == NULL) { return NULL; } if (head->val == val) { struct ListNode* p = head; head = head->next; free(p); return head; } struct ListNode* p = head; while (p->next != NULL && p->next->val != val) { p = p->next; } if (p->next != NULL) { struct ListNode* q = p->next; p->next = q->next; free(q); } return head; } //销毁链表 void destroyList(struct ListNode* head) { struct ListNode* p = head; while (p != NULL) { struct ListNode* q = p->next; free(p); p = q; } } int main() { int nums[] = {1, 2, 3, 4, 5}; int numsSize = sizeof(nums) / sizeof(int); struct ListNode* head = createList(nums, numsSize); traverseList(head); head = insertList(head, 6); traverseList(head); head = deleteList(head, 3); traverseList(head); destroyList(head); return 0; } ``` 以上代码演示了如何使用结构体和指针实现链表的创建、遍历、插入、删除和销毁等操作。 如果您想深入学习数据结构算法,可以参考以下学习资源: - 《算法导论》 - 《数据结构与算法分析》 - 《数据结构与算法》(王道考研系列) - LeetCode网站上的数据结构算法题目

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员老茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值