数据结构第一轮复习--第六章图包含代码

图的存储--邻接表法

复制代码
//"顶点"
typedef struct VNode{
	VertexType data;	//顶点信息
	ArcNode *first; 	//第一条边/弧 
}VNode,AdjList[MaxVertexNum]; 

//用邻接表存储的图
typedef struct{
	AdjList data;	//顶点信息
	int vexnum,arcnum; 
}ALGraph; 

//"边/弧 " 
typedef struct ArcNode{
	int adjvex;	//边/弧指向哪个节点 
	struct ArcNode *next;//指向下一条弧的指针 
	//InfoType info; //边权值 
}ArcNode;

图的广度优先遍历--BFS算法

复制代码
//图的广度优先算法--BFS算法
bool visited[MAX_VERTEX_NUM]//访问标记数组 
void BFSTraverse(Graph G){	//对图G进行广度优先算法 
	for(i=0;i<G.vexnum;++i)	
		visited[i]=FALSE;	//访问标记数组初始化 
	InitQueue(Q);		//初始化辅助队列Q 
	for(i=0;i<G.vexnum;++i)	//从0号顶点开始遍历 
		if(!visited[i])	//对每个连同分量调用一次BFS 
			BFS(G,i);	//vi未访问过,从vi开始BFS 
}
// 广度优先遍历 通过辅助队列实现 
void BFS(Graph G,int v){	//从顶点v出发,广度优先遍历图G 
	visit(v);				//访问初始顶点v 
	visited[v]=TRUE;		//对v做已访问标记 
	EnQueue(Q,v);			//顶点v入队列Q 
	while(!isEmpty(Q)){		
		DeQueue(Q,v);		//顶点v出队列 
			for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))//检测v所有的邻接点 
				if(!visited[w]){	//w为v的尚未访问的邻接顶点 
					visit(w);		//访问顶点w 
					visited[w]=TRUE;	//对w做已访问标记 
					EnQueue(Q,w);//顶点w入队列 
				}//if 
	}//while 
} 

图的深度优先遍历-DFS算法

复制代码
//图的深度优先遍历算法  通过栈实现 
bool visited[MAX_VERTEX_NUM]//访问标记数组 
void DFSTraverse(Graph G){	//对图G进行深度优先算法 
	for(i=0;i<G.vexnum;++i)	
		visited[i]=FALSE;	//访问标记数组初始化 
	for(i=0;i<G.vexnum;++i)	//从0号顶点开始遍历 
		if(!visited[i])	//对每个连同分量调用一次BFS 
			DFS(G,i);	//vi未访问过,从vi开始BFS 
}

void DFS(Graph G,int v){	//从顶点v出发,深度优先遍历图G 
	visit(v);
	visited[v]=TRUE;
	for(w=FirseNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
		if(!visited[w]){	//w为v尚未访问的邻接节点 
			DFS(G,W);
		}		
}

Prim算法实现最小生成树

复制代码
#include <stdio.h>
#include <limits.h> // 提供 INT_MAX 的定义

#define INF INT_MAX // 定义无穷大值
#define V 5 // 图中的顶点数量

// 函数:找到未加入MST的顶点中键值最小的顶点
int minKey(int key[], int mstSet[]) {
    int min = INF, minIndex; // 初始化最小值为无穷大

    // 遍历所有顶点
    for (int v = 0; v < V; v++) {
        // 如果顶点未加入MST且当前键值小于最小值,则更新最小值和最小值索引
        if (!mstSet[v] && key[v] < min) {
            min = key[v];
            minIndex = v;
        }
    }
    return minIndex; // 返回键值最小的顶点索引
}

// 函数:打印构造的MST
void printMST(int parent[], int graph[V][V]) {
    printf("Edge \tWeight\n"); // 打印表头
    // 遍历所有顶点(从1开始,因为0是根节点,没有父节点)
    for (int i = 1; i < V; i++) {
        // 打印边和权重
        printf("%d - %d \t%d \n", parent[i], i, graph[i][parent[i]]);
    }
}

// 函数:使用Prim算法构造并打印MST
void primMST(int graph[V][V]) {
    int parent[V];  // 用于存储MST的数组
    int key[V];     // 用于存储每个顶点的键值,键值代表该顶点到MST的最小距离
    int mstSet[V];  // 布尔数组,用于跟踪顶点是否已加入MST

    // 初始化所有键值为无穷大,所有顶点都未加入MST
    for (int i = 0; i < V; i++) {
        key[i] = INF;
        mstSet[i] = 0;
    }

    key[0] = 0;       // 将第一个顶点的键值设为0,以便从它开始构建MST
    parent[0] = -1;   // 第一个顶点没有父节点

    // 构建MST,需要V-1条边
    for (int count = 0; count < V - 1; count++) {
        // 找到键值最小且未加入MST的顶点
        int u = minKey(key, mstSet);
        mstSet[u] = 1; // 将该顶点加入MST

        // 更新相邻顶点的键值
        for (int v = 0; v < V; v++) {
            // 如果存在边,且顶点v未加入MST,且边的权重小于当前键值
            if (graph[u][v] && !mstSet[v] && graph[u][v] < key[v]) {
                parent[v] = u; // 更新父节点
                key[v] = graph[u][v]; // 更新键值
            }
        }
    }

    // 打印构造的MST
    printMST(parent, graph);
}

int main() {
    // 定义一个图的邻接矩阵表示
    int graph[V][V] = {
        {0, 2, 0, 6, 0},
        {2, 0, 3, 8, 5},
        {0, 3, 0, 0, 7},
        {6, 8, 0, 0, 9},
        {0, 5, 7, 9, 0}
    };

    // 调用Prim算法函数
    primMST(graph);
    return 0;
}

Kruskal算法实现最小生成树

复制代码
#include <stdio.h>
#include <stdlib.h>

#define MAX_EDGES 100 // 定义最大边数

// 定义边的结构体
typedef struct {
    int u, v, weight; // 边的两个顶点和权重
} Edge;

// 定义图的结构体
typedef struct {
    int V, E; // 顶点数和边数
    Edge edges[MAX_EDGES]; // 存储所有边
} Graph;

// 定义并查集的结构体
typedef struct {
    int parent[MAX_EDGES]; // 父节点数组
    int rank[MAX_EDGES];   // 秩数组,用于优化合并操作
} DisjointSet;

// 创建图的函数
Graph* createGraph(int V, int E) {
    Graph* graph = (Graph*)malloc(sizeof(Graph)); // 分配内存
    graph->V = V; // 设置顶点数
    graph->E = E; // 设置边数
    return graph; // 返回图的指针
}

// 查找函数,用于并查集
int find(DisjointSet* ds, int i) {
    if (ds->parent[i] != i) { // 如果当前节点不是根节点
        ds->parent[i] = find(ds, ds->parent[i]); // 路径压缩,递归查找根节点
    }
    return ds->parent[i]; // 返回根节点
}

// 合并函数,用于并查集
void unionSets(DisjointSet* ds, int x, int y) {
    x = find(ds, x); // 找到x的根节点
    y = find(ds, y); // 找到y的根节点
    if (ds->rank[x] < ds->rank[y]) { // 如果x的秩小于y的秩
        ds->parent[x] = y; // 将x的根节点设置为y的根节点
    } else if (ds->rank[x] > ds->rank[y]) { // 如果x的秩大于y的秩
        ds->parent[y] = x; // 将y的根节点设置为x的根节点
    } else { // 如果秩相等
        ds->parent[y] = x; // 将y的根节点设置为x的根节点
        ds->rank[x]++; // 增加x的秩
    }
}

// 比较函数,用于qsort排序
int compare(const void* a, const void* b) {
    Edge* edge1 = (Edge*)a; // 将void指针转换为Edge指针
    Edge* edge2 = (Edge*)b; // 将void指针转换为Edge指针
    return edge1->weight - edge2->weight; // 按权重升序排序
}

// Kruskal算法实现
void kruskalMST(Graph* graph) {
    DisjointSet ds; // 创建并查集
    int V = graph->V; // 获取顶点数
    int E = graph->E; // 获取边数

    // 初始化并查集
    for (int i = 0; i < V; i++) {
        ds.parent[i] = i; // 每个节点的父节点初始化为自身
        ds.rank[i] = 0;   // 每个节点的秩初始化为0
    }

    // 按边的权重排序
    qsort(graph->edges, E, sizeof(Edge), compare);

    int e = 0; // 已选择的边数
    int i = 0; // 当前处理的边的索引

    // 构建最小生成树
    while (e < V - 1) { // 最小生成树需要V-1条边
        Edge nextEdge = graph->edges[i++]; // 获取当前边
        int x = find(&ds, nextEdge.u); // 找到边的一个顶点的根节点
        int y = find(&ds, nextEdge.v); // 找到边的另一个顶点的根节点

        if (x != y) { // 如果两个顶点不在同一个集合中
            printf("%d - %d : %d\n", nextEdge.u, nextEdge.v, nextEdge.weight); // 输出边
            unionSets(&ds, x, y); // 合并两个集合
            e++; // 增加已选择的边数
        }
    }
}

int main() {
    int V = 4; // 顶点数
    int E = 5; // 边数
    Graph* graph = createGraph(V, E); // 创建图

    // 添加边
    graph->edges[0].u = 0;
    graph->edges[0].v = 1;
    graph->edges[0].weight = 10;

    graph->edges[1].u = 0;
    graph->edges[1].v = 2;
    graph->edges[1].weight = 6;

    graph->edges[2].u = 0;
    graph->edges[2].v = 3;
    graph->edges[2].weight = 5;

    graph->edges[3].u = 1;
    graph->edges[3].v = 3;
    graph->edges[3].weight = 15;

    graph->edges[4].u = 2;
    graph->edges[4].v = 3;
    graph->edges[4].weight = 4;

    // 调用Kruskal算法
    kruskalMST(graph);

    return 0;
}

BFS算法解决无权图的单源最短路径问题

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h> // 用于 INT_MAX

#define MAX_VERTEX_NUM 20 // 假设图的最大顶点数

// 邻接表的边表节点
typedef struct ArcNode {
    int adjvex; // 邻接点域,存储该顶点对应的下标
    struct ArcNode *next; // 链域,指向下一个邻接点
} ArcNode;

// 邻接表的表头节点
typedef struct VNode {
    int data; // 顶点信息
    ArcNode *first; // 边表头指针
} VNode, ALGraph[MAX_VERTEX_NUM];

// 队列结构
typedef struct {
    int data[MAX_VERTEX_NUM];
    int front, rear;
} Queue;

// 初始化队列
void InitQueue(Queue *Q) {
    Q->front = Q->rear = 0;
}

// 队列是否为空
bool IsEmpty(Queue *Q) {
    return Q->front == Q->rear;
}

// 入队操作
void EnQueue(Queue *Q, int v) {
    Q->data[Q->rear++] = v;
}

// 出队操作
int DeQueue(Queue *Q) {
    return Q->data[Q->front++];
}

// 创建邻接表的边表节点
ArcNode* CreateArcNode(int adjvex) {
    ArcNode *newNode = (ArcNode *)malloc(sizeof(ArcNode));
    newNode->adjvex = adjvex;
    newNode->next = NULL;
    return newNode;
}

// 广度优先搜索求单源最短路径
void BFS_ShortestPath(ALGraph G, int source, int dist[]) {
    bool visited[MAX_VERTEX_NUM] = {false}; // 访问标记数组
    Queue Q;
    InitQueue(&Q);
    EnQueue(&Q, source);
    visited[source] = true;
    dist[source] = 0; // 源点到自身的距离为0

    while (!IsEmpty(&Q)) {
        int v = DeQueue(&Q);
        ArcNode *p = G[v].first;
        while (p != NULL) {
            int w = p->adjvex;
            if (!visited[w]) {
                visited[w] = true;
                dist[w] = dist[v] + 1; // 源点到w的距离等于源点到v的距离加1
                EnQueue(&Q, w);
            }
            p = p->next;
        }
    }
}

int main() {
    ALGraph G; // 邻接表
    int n; // 顶点数
    int e; // 边数
    int source; // 源点

    // 输入顶点数和边数
    printf("Enter number of vertices and edges: ");
    scanf("%d %d", &n, &e);

    // 初始化邻接表
    for (int i = 0; i < n; ++i) {
        G[i].data = i;
        G[i].first = NULL;
    }

    // 输入边
    printf("Enter edges (u v):\n");
    for (int i = 0; i < e; ++i) {
        int u, v;
        scanf("%d %d", &u, &v);
        ArcNode *newNode = CreateArcNode(v);
        newNode->next = G[u].first;
        G[u].first = newNode;
    }

    // 输入源点
    printf("Enter source vertex: ");
    scanf("%d", &source);

    // 初始化距离数组
    int dist[MAX_VERTEX_NUM];
    for (int i = 0; i < n; ++i) {
        dist[i] = INT_MAX; // 初始化为无穷大
    }

    // 调用BFS求单源最短路径
    BFS_ShortestPath(G, source, dist);

    // 输出结果
    printf("Shortest distances from source vertex %d:\n", source);
    for (int i = 0; i < n; ++i) {
        if (dist[i] != INT_MAX) {
            printf("Vertex %d: %d\n", i, dist[i]);
        } else {
            printf("Vertex %d: unreachable\n", i);
        }
    }

    return 0;
}

代码二书上代码

复制代码
//BFS算法解决单源最短路径问题
void BFS_MIN_Distance(Graph G,int u){
	//d[i]表示从u到i节点的最短路径
	for(i=0;i<G.vexnum;++i)
		d[i]=INT_MAX;//初始化路径长度,即到所有的节点距离都是无穷 INT_MAX表示无穷 
	visited[u]=TRUE;d[u]=0; //初始化源点 
	EnQueue(Q,u);
	while(!isEmpty(Q)){		//BFS
		DeQueue(Q,u);		//对头元素u出队列 
			for(w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w))//检测v所有的邻接点 
				if(!visited[w]){	//w为u的尚未访问的邻接顶点 
					visited[w]=TRUE;	//对w做已访问标记 
					d[w]=d[u]+1;	//路径长度+1 
					EnQueue(Q,w);//顶点w入队列 
				}//if 
	}//while 
} 

Dijkstra算法求单源最短路径问题

复制代码
#include <stdio.h> // 包含标准输入输出库
#include <stdlib.h> // 包含标准库函数
#include <limits.h> // 包含INT_MAX等宏定义

#define V 5 // 定义图中顶点的数量

// 找到距离源点最近的未处理顶点
int minDistance(int dist[], int sptSet[], int n) {
    int min = INT_MAX, min_index; // 初始化最小距离为INT_MAX,用于比较
    for (int v = 0; v < n; v++) { // 遍历所有顶点
        if (sptSet[v] == 0 && dist[v] <= min) { // 如果顶点未处理且当前距离小于等于最小值
            min = dist[v]; // 更新最小距离
            min_index = v; // 更新最小距离对应的顶点索引
        }
    }
    return min_index; // 返回距离源点最近的未处理顶点的索引
}

// 打印最短路径结果
void printSolution(int dist[], int n) {
    printf("Vertex\t Distance from Source\n"); // 打印表头
    for (int i = 0; i < n; i++) { // 遍历所有顶点
        printf("%d \t\t %d\n", i, dist[i]); // 打印顶点编号和到源点的最短距离
    }
}

// Dijkstra算法实现
void dijkstra(int graph[V][V], int src) {
    int dist[V]; // 保存从源点到每个顶点的最短路径
    int sptSet[V]; // 保存每个顶点是否已经被处理(最短路径集合)

    // 初始化所有距离为无穷大,sptSet[]为false
    for (int i = 0; i < V; i++) {
        dist[i] = INT_MAX; // 初始距离设为无穷大
        sptSet[i] = 0; // 初始时所有顶点都未处理
    }

    // 源点到自身的距离是0
    dist[src] = 0; // 源点到自身的距离为0

    for (int count = 0; count < V - 1; count++) { // 迭代V-1次,找到所有顶点的最短路径
        // 选择距离源点最近的未处理顶点
        int u = minDistance(dist, sptSet, V); // 调用minDistance函数找到最近的顶点
        sptSet[u] = 1; // 标记该顶点已处理

        // 更新相邻顶点的距离
        for (int v = 0; v < V; v++) { // 遍历所有顶点
            // 如果顶点v未处理,且u到v有边,且u到v的路径长度小于当前已知的最短路径
            if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {
                dist[v] = dist[u] + graph[u][v]; // 更新v的最短路径
            }
        }
    }

    printSolution(dist, V); // 打印最终的最短路径结果
}

int main() {
    /* 假设图的邻接矩阵如下:
       例图是一个有向图,顶点编号为0到4
       0 -- 4 --> 1
       |         ^
       8         |
       |         1
       v         |
       2 -- 2 --> 3 -- 7 --> 4
       3         6
       ^         |
       |         |
       7 --------*/
    int graph[V][V] = {{ 0, 4, 0, 0, 0 }, // 邻接矩阵表示图的边权重
                       { 0, 0, 8, 0, 0 },
                       { 0, 0, 0, 2, 0 },
                       { 0, 0, 0, 0, 7 },
                       { 0, 0, 0, 0, 0 }};

    dijkstra(graph, 0); // 从顶点0开始计算最短路径
    return 0; // 程序结束
}

Floyd算法解决单源最短路径问题

复制代码
#include <stdio.h>
#include <limits.h> // 包含INT_MAX等宏定义

#define V 5 // 定义图中顶点的数量

// 打印最短路径矩阵
void printSolution(int dist[][V]) {
    printf("The following matrix shows the shortest distances between every pair of vertices:\n");
    for (int i = 0; i < V; i++) {
        for (int j = 0; j < V; j++) {
            if (dist[i][j] == INT_MAX) // 如果距离为INT_MAX,表示没有路径
                printf("%7s", "INF");
            else
                printf("%7d", dist[i][j]); // 打印距离
        }
        printf("\n");
    }
}

// Floyd算法实现
void floydWarshall(int graph[][V]) {
    int dist[V][V]; // 创建一个二维数组dist,用于存储最短路径

    // 初始化dist矩阵为图的邻接矩阵
    for (int i = 0; i < V; i++)
        for (int j = 0; j < V; j++)
            dist[i][j] = graph[i][j];

    // 动态规划求解所有顶点对之间的最短路径
    for (int k = 0; k < V; k++) { // k表示中间顶点
        for (int i = 0; i < V; i++) { // i表示源顶点
            for (int j = 0; j < V; j++) { // j表示目标顶点
                // 如果i到j通过k的路径更短,则更新dist[i][j]
                if (dist[i][k] + dist[k][j] < dist[i][j])
                    dist[i][j] = dist[i][k] + dist[k][j];
            }
        }
    }

    // 打印最终的最短路径矩阵
    printSolution(dist);
}

int main() {
    /* 假设图的邻接矩阵如下:
       例图是一个有向图,顶点编号为0到4
       0 -- 5 --> 1
       |         ^
       8         |
       |         2
       v         |
       2 -- 3 --> 3 -- 4 --> 4
       3         6
       ^         |
       |         |
       7 --------*/
    int graph[V][V] = { { 0, 5, INT_MAX, 8, INT_MAX },
                        { INT_MAX, 0, 2, INT_MAX, INT_MAX },
                        { INT_MAX, INT_MAX, 0, 3, 4 },
                        { INT_MAX, INT_MAX, INT_MAX, 0, 6 },
                        { 7, INT_MAX, INT_MAX, INT_MAX, 0 } };

    // 调用Floyd算法
    floydWarshall(graph);

    return 0; // 程序结束
}

总结

逆拓扑排序的实现(DFS算法)

复制代码
//逆拓扑排序的实现(DFS算法)
void DFSTraverse(Graph G){ //对图G进行深度优先遍历 
	for(v=0;v<G.vexnum;++v)
		visited[v]=false;//初始化已访问标记数据 
	for(v=0;v<G.vexnum;++v)//本代码从0开始遍历 
		if(!visited[v])
			DFS(G,v);
}
void DFS(Graph G,int v){	//从顶点v出发,深度优先遍历图G 
	visited[v]=true;	//设已访问标记 
	for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
		if(!visited[w]){	//w为u的尚未访问的邻接节点 
			DFS(G,w);
		}
	print(v);
    //在所有邻接顶点都被访问后,打印顶点 v。
    //这一步实现了逆拓扑排序,因为顶点 v 在其所有邻接顶点之后被打印。
} 

拓扑排序的实现

复制代码
//拓扑排序的实现
bool TopologicalSort(Graph G){
	InitStack(S);	//初始化栈,存储入度为0的顶点
	for(int i=0;i<G.vexnum;i++)
		if(indegree[i]==0)
			Push(S,i);	//将所有入度为0的顶点进栈
	int count=0; 	//计数,记录当前已经输出的顶点数
	while(!IsEmpty(S)){	//栈不空,则存在入度为0的顶点
		Pop(S,i);//栈顶元素出栈
		print[count++]=i;//输出顶点i
		for(p=G.vertices[i].firstarc;p;p=p->nextarc){
			//将所有i指向的顶点的入度减1,并且将入度减为0的顶点压入栈 S
			v=p->adjvex;
			if(!(--indegree[v]))
				Push(S,v);//入度为0,则入栈 
		} 
		
	} //while
	if(count<G.vexnum)
		return false;//排序失败,有向图中有回路
	else
		return true;//拓扑排序成功 
}
相关推荐
永恒迷星.by1 小时前
文件操作(c语言)
c语言·c++·算法·文件操作
还有你Y1 小时前
MIMO预编码与检测算法的对比
算法·预编码算法
凯强同学1 小时前
第十四届蓝桥杯大赛软件赛省赛Python 大学 C 组:7.翻转
python·算法·蓝桥杯
记得早睡~2 小时前
leetcode51-N皇后
javascript·算法·leetcode·typescript
ylfhpy3 小时前
Java面试黄金宝典33
java·开发语言·数据结构·面试·职场和发展·排序算法
lancyu3 小时前
C语言--插入排序
c语言·算法·排序算法
点云SLAM3 小时前
C++20新增内容
c++·算法·c++20·c++ 标准库
独好紫罗兰4 小时前
洛谷题单3-P1217 [USACO1.5] 回文质数 Prime Palindromes-python-流程图重构
开发语言·python·算法
独好紫罗兰4 小时前
洛谷题单2-P1424 小鱼的航程(改进版)-python-流程图重构
开发语言·python·算法
qystca5 小时前
蓝桥云客---九宫幻方
算法·深度优先·图论