C语言需要掌握的基础知识点之图

C语言需要掌握的基础知识点之图

图是一种非常重要的非线性数据结构,由顶点的集合和顶点之间边的集合组成。以下是C语言中图的详细介绍和实现。

图的基本概念

图的定义

顶点(Vertex):图的基本单元

边(Edge):顶点之间的连接

有向图:边有方向

无向图:边没有方向

权重图:边带有权值

图的表示方法

邻接矩阵

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

#define MAX_VERTICES 100
#define INF INT_MAX  // 表示无穷大

typedef struct Graph {
    int numVertices;
    int adjacencyMatrix[MAX_VERTICES][MAX_VERTICES];
} Graph;

// 初始化图
Graph* createGraph(int vertices) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    graph->numVertices = vertices;
    
    // 初始化邻接矩阵
    for(int i = 0; i < vertices; i++) {
        for(int j = 0; j < vertices; j++) {
            if(i == j)
                graph->adjacencyMatrix[i][j] = 0;  // 对角线为0
            else
                graph->adjacencyMatrix[i][j] = INF; // 初始为无穷大
        }
    }
    return graph;
}

// 添加边(无向图)
void addEdgeUndirected(Graph* graph, int src, int dest, int weight) {
    graph->adjacencyMatrix[src][dest] = weight;
    graph->adjacencyMatrix[dest][src] = weight;
}

// 添加边(有向图)
void addEdgeDirected(Graph* graph, int src, int dest, int weight) {
    graph->adjacencyMatrix[src][dest] = weight;
}

// 打印邻接矩阵
void printGraph(Graph* graph) {
    printf("邻接矩阵:\n");
    for(int i = 0; i < graph->numVertices; i++) {
        for(int j = 0; j < graph->numVertices; j++) {
            if(graph->adjacencyMatrix[i][j] == INF)
                printf("INF ");
            else
                printf("%3d ", graph->adjacencyMatrix[i][j]);
        }
        printf("\n");
    }
}

邻接表

c 复制代码
// 邻接表节点
typedef struct AdjListNode {
    int dest;
    int weight;
    struct AdjListNode* next;
} AdjListNode;

// 邻接表
typedef struct AdjList {
    AdjListNode* head;
} AdjList;

// 图结构(邻接表)
typedef struct GraphList {
    int numVertices;
    AdjList* array;
} GraphList;

// 创建邻接表节点
AdjListNode* createAdjListNode(int dest, int weight) {
    AdjListNode* newNode = (AdjListNode*)malloc(sizeof(AdjListNode));
    newNode->dest = dest;
    newNode->weight = weight;
    newNode->next = NULL;
    return newNode;
}

// 创建图(邻接表)
GraphList* createGraphList(int vertices) {
    GraphList* graph = (GraphList*)malloc(sizeof(GraphList));
    graph->numVertices = vertices;
    
    graph->array = (AdjList*)malloc(vertices * sizeof(AdjList));
    
    for(int i = 0; i < vertices; i++) {
        graph->array[i].head = NULL;
    }
    return graph;
}

// 添加边(无向图-邻接表)
void addEdgeListUndirected(GraphList* graph, int src, int dest, int weight) {
    // 添加从src到dest的边
    AdjListNode* newNode = createAdjListNode(dest, weight);
    newNode->next = graph->array[src].head;
    graph->array[src].head = newNode;
    
    // 添加从dest到src的边
    newNode = createAdjListNode(src, weight);
    newNode->next = graph->array[dest].head;
    graph->array[dest].head = newNode;
}

// 添加边(有向图-邻接表)
void addEdgeListDirected(GraphList* graph, int src, int dest, int weight) {
    AdjListNode* newNode = createAdjListNode(dest, weight);
    newNode->next = graph->array[src].head;
    graph->array[src].head = newNode;
}

// 打印邻接表
void printGraphList(GraphList* graph) {
    printf("邻接表:\n");
    for(int i = 0; i < graph->numVertices; i++) {
        AdjListNode* temp = graph->array[i].head;
        printf("顶点 %d: ", i);
        while(temp) {
            printf("-> %d(w:%d) ", temp->dest, temp->weight);
            temp = temp->next;
        }
        printf("\n");
    }
}

图的遍历算法

深度优先搜索(DFS)

c 复制代码
// DFS递归实现(邻接矩阵)
void DFS_Matrix(Graph* graph, int vertex, int visited[]) {
    visited[vertex] = 1;
    printf("%d ", vertex);
    
    for(int i = 0; i < graph->numVertices; i++) {
        if(graph->adjacencyMatrix[vertex][i] != INF && !visited[i]) {
            DFS_Matrix(graph, i, visited);
        }
    }
}

void DFSTraversal(Graph* graph, int startVertex) {
    int visited[MAX_VERTICES] = {0};
    printf("DFS遍历: ");
    DFS_Matrix(graph, startVertex, visited);
    printf("\n");
}

广度优先搜索(BFS)

c 复制代码
// 队列实现
typedef struct Queue {
    int items[MAX_VERTICES];
    int front;
    int rear;
} Queue;

Queue* createQueue() {
    Queue* q = (Queue*)malloc(sizeof(Queue));
    q->front = -1;
    q->rear = -1;
    return q;
}

int isEmpty(Queue* q) {
    return q->rear == -1;
}

void enqueue(Queue* q, int value) {
    if(q->rear == MAX_VERTICES - 1)
        printf("队列已满\n");
    else {
        if(q->front == -1)
            q->front = 0;
        q->rear++;
        q->items[q->rear] = value;
    }
}

int dequeue(Queue* q) {
    int item;
    if(isEmpty(q)) {
        printf("队列为空\n");
        item = -1;
    } else {
        item = q->items[q->front];
        q->front++;
        if(q->front > q->rear) {
            q->front = q->rear = -1;
        }
    }
    return item;
}

// BFS遍历(邻接矩阵)
void BFS_Matrix(Graph* graph, int startVertex) {
    int visited[MAX_VERTICES] = {0};
    Queue* q = createQueue();
    
    visited[startVertex] = 1;
    enqueue(q, startVertex);
    
    printf("BFS遍历: ");
    
    while(!isEmpty(q)) {
        int currentVertex = dequeue(q);
        printf("%d ", currentVertex);
        
        for(int i = 0; i < graph->numVertices; i++) {
            if(graph->adjacencyMatrix[currentVertex][i] != INF && !visited[i]) {
                visited[i] = 1;
                enqueue(q, i);
            }
        }
    }
    printf("\n");
}

图的最小生成树算法

Prim算法

c 复制代码
// Prim算法求最小生成树
void primMST(Graph* graph) {
    int parent[MAX_VERTICES];    // 存储MST
    int key[MAX_VERTICES];       // 权值数组
    int mstSet[MAX_VERTICES];    // 标记顶点是否在MST中
    
    // 初始化
    for(int i = 0; i < graph->numVertices; i++) {
        key[i] = INF;
        mstSet[i] = 0;
    }
    
    key[0] = 0;        // 从第一个顶点开始
    parent[0] = -1;    // 第一个顶点是根节点
    
    for(int count = 0; count < graph->numVertices - 1; count++) {
        // 选择最小权值的顶点
        int min = INF, minIndex;
        for(int v = 0; v < graph->numVertices; v++) {
            if(!mstSet[v] && key[v] < min) {
                min = key[v];
                minIndex = v;
            }
        }
        
        int u = minIndex;
        mstSet[u] = 1;
        
        // 更新相邻顶点的权值
        for(int v = 0; v < graph->numVertices; v++) {
            if(graph->adjacencyMatrix[u][v] != INF && !mstSet[v] && 
               graph->adjacencyMatrix[u][v] < key[v]) {
                parent[v] = u;
                key[v] = graph->adjacencyMatrix[u][v];
            }
        }
    }
    
    // 打印MST
    printf("Prim算法 - 最小生成树:\n");
    for(int i = 1; i < graph->numVertices; i++) {
        printf("%d - %d : %d\n", parent[i], i, graph->adjacencyMatrix[i][parent[i]]);
    }
}

Kruskal算法

c 复制代码
// 边的结构体
typedef struct Edge {
    int src, dest, weight;
} Edge;

// 并查集结构
typedef struct Subset {
    int parent;
    int rank;
} Subset;

// 查找根节点
int find(Subset subsets[], int i) {
    if(subsets[i].parent != i)
        subsets[i].parent = find(subsets, subsets[i].parent);
    return subsets[i].parent;
}

// 合并两个集合
void unionSets(Subset subsets[], int x, int y) {
    int rootX = find(subsets, x);
    int rootY = find(subsets, y);
    
    if(subsets[rootX].rank < subsets[rootY].rank)
        subsets[rootX].parent = rootY;
    else if(subsets[rootX].rank > subsets[rootY].rank)
        subsets[rootY].parent = rootX;
    else {
        subsets[rootY].parent = rootX;
        subsets[rootX].rank++;
    }
}

// 比较函数(用于排序)
int compareEdges(const void* a, const void* b) {
    Edge* edge1 = (Edge*)a;
    Edge* edge2 = (Edge*)b;
    return edge1->weight - edge2->weight;
}

// Kruskal算法
void kruskalMST(Graph* graph) {
    int V = graph->numVertices;
    Edge result[V];  // 存储MST结果
    int e = 0;       // 结果数组的索引
    int i = 0;       // 排序后边的索引
    
    // 获取所有边
    Edge* edges = (Edge*)malloc(V * V * sizeof(Edge));
    int edgeCount = 0;
    
    for(int u = 0; u < V; u++) {
        for(int v = u + 1; v < V; v++) {
            if(graph->adjacencyMatrix[u][v] != INF) {
                edges[edgeCount].src = u;
                edges[edgeCount].dest = v;
                edges[edgeCount].weight = graph->adjacencyMatrix[u][v];
                edgeCount++;
            }
        }
    }
    
    // 按权值排序
    qsort(edges, edgeCount, sizeof(Edge), compareEdges);
    
    // 分配内存给并查集
    Subset* subsets = (Subset*)malloc(V * sizeof(Subset));
    
    // 初始化并查集
    for(int v = 0; v < V; v++) {
        subsets[v].parent = v;
        subsets[v].rank = 0;
    }
    
    // 处理每条边
    while(e < V - 1 && i < edgeCount) {
        Edge nextEdge = edges[i++];
        
        int x = find(subsets, nextEdge.src);
        int y = find(subsets, nextEdge.dest);
        
        // 如果不形成环,加入MST
        if(x != y) {
            result[e++] = nextEdge;
            unionSets(subsets, x, y);
        }
    }
    
    // 打印MST
    printf("Kruskal算法 - 最小生成树:\n");
    int minimumCost = 0;
    for(i = 0; i < e; i++) {
        printf("%d - %d : %d\n", result[i].src, result[i].dest, result[i].weight);
        minimumCost += result[i].weight;
    }
    printf("最小生成树总权值: %d\n", minimumCost);
    
    free(edges);
    free(subsets);
}

最短路径算法

Dijkstra算法

c 复制代码
// Dijkstra算法求单源最短路径
void dijkstra(Graph* graph, int src) {
    int dist[MAX_VERTICES];    // 最短距离数组
    int visited[MAX_VERTICES]; // 已访问标记
    
    // 初始化
    for(int i = 0; i < graph->numVertices; i++) {
        dist[i] = INF;
        visited[i] = 0;
    }
    dist[src] = 0;
    
    for(int count = 0; count < graph->numVertices - 1; count++) {
        // 选择最小距离的顶点
        int min = INF, minIndex;
        for(int v = 0; v < graph->numVertices; v++) {
            if(!visited[v] && dist[v] <= min) {
                min = dist[v];
                minIndex = v;
            }
        }
        
        int u = minIndex;
        visited[u] = 1;
        
        // 更新相邻顶点的距离
        for(int v = 0; v < graph->numVertices; v++) {
            if(!visited[v] && graph->adjacencyMatrix[u][v] != INF && 
               dist[u] != INF && dist[u] + graph->adjacencyMatrix[u][v] < dist[v]) {
                dist[v] = dist[u] + graph->adjacencyMatrix[u][v];
            }
        }
    }
    
    // 打印最短路径
    printf("Dijkstra算法 - 从顶点%d到各顶点的最短距离:\n", src);
    for(int i = 0; i < graph->numVertices; i++) {
        if(dist[i] == INF)
            printf("到顶点%d: 不可达\n", i);
        else
            printf("到顶点%d: %d\n", i, dist[i]);
    }
}

Floyd-Warshall算法

c 复制代码
// Floyd-Warshall算法求所有顶点对最短路径
void floydWarshall(Graph* graph) {
    int dist[MAX_VERTICES][MAX_VERTICES];
    int V = graph->numVertices;
    
    // 初始化距离矩阵
    for(int i = 0; i < V; i++) {
        for(int j = 0; j < V; j++) {
            dist[i][j] = graph->adjacencyMatrix[i][j];
        }
    }
    
    // Floyd-Warshall算法核心
    for(int k = 0; k < V; k++) {
        for(int i = 0; i < V; i++) {
            for(int j = 0; j < V; j++) {
                if(dist[i][k] != INF && dist[k][j] != INF && 
                   dist[i][k] + dist[k][j] < dist[i][j]) {
                    dist[i][j] = dist[i][k] + dist[k][j];
                }
            }
        }
    }
    
    // 打印结果
    printf("Floyd-Warshall算法 - 所有顶点对最短距离:\n");
    for(int i = 0; i < V; i++) {
        for(int j = 0; j < V; j++) {
            if(dist[i][j] == INF)
                printf("INF ");
            else
                printf("%3d ", dist[i][j]);
        }
        printf("\n");
    }
}

拓扑排序

c 复制代码
// 拓扑排序(邻接表)
void topologicalSortUtil(GraphList* graph, int v, int visited[], int stack[], int* top) {
    visited[v] = 1;
    
    AdjListNode* temp = graph->array[v].head;
    while(temp != NULL) {
        int adjVertex = temp->dest;
        if(!visited[adjVertex]) {
            topologicalSortUtil(graph, adjVertex, visited, stack, top);
        }
        temp = temp->next;
    }
    
    stack[++(*top)] = v;
}

void topologicalSort(GraphList* graph) {
    int stack[MAX_VERTICES];
    int top = -1;
    int visited[MAX_VERTICES] = {0};
    
    for(int i = 0; i < graph->numVertices; i++) {
        if(!visited[i]) {
            topologicalSortUtil(graph, i, visited, stack, &top);
        }
    }
    
    printf("拓扑排序: ");
    while(top >= 0) {
        printf("%d ", stack[top--]);
    }
    printf("\n");
}

完整示例程序

c 复制代码
int main() {
    int V = 5;  // 顶点数
    
    // 创建图(邻接矩阵)
    Graph* graph = createGraph(V);
    
    // 添加边(无向图)
    addEdgeUndirected(graph, 0, 1, 2);
    addEdgeUndirected(graph, 0, 3, 6);
    addEdgeUndirected(graph, 1, 2, 3);
    addEdgeUndirected(graph, 1, 3, 8);
    addEdgeUndirected(graph, 1, 4, 5);
    addEdgeUndirected(graph, 2, 4, 7);
    addEdgeUndirected(graph, 3, 4, 9);
    
    // 打印图
    printGraph(graph);
    
    // 图遍历
    DFSTraversal(graph, 0);
    BFS_Matrix(graph, 0);
    
    // 最小生成树
    primMST(graph);
    kruskalMST(graph);
    
    // 最短路径
    dijkstra(graph, 0);
    floydWarshall(graph);
    
    // 创建有向图进行拓扑排序
    GraphList* graphList = createGraphList(6);
    addEdgeListDirected(graphList, 5, 2, 1);
    addEdgeListDirected(graphList, 5, 0, 1);
    addEdgeListDirected(graphList, 4, 0, 1);
    addEdgeListDirected(graphList, 4, 1, 1);
    addEdgeListDirected(graphList, 2, 3, 1);
    addEdgeListDirected(graphList, 3, 1, 1);
    
    printGraphList(graphList);
    topologicalSort(graphList);
    
    // 释放内存
    free(graph);
    
    return 0;
}

图的应用场景

相关推荐
Lethehong3 小时前
告别显卡焦虑:Wan2.1+cpolar让AI视频创作走进普通家庭
cpolar·1024程序员节
C灿灿数模3 小时前
2025MathorCup大数据竞赛A题B题选题建议与分析,思路模型
1024程序员节
木法星人3 小时前
Ubuntu安装nvm(无需梯子自动连接github下载安装)
ubuntu·nvm·1024程序员节
草莓base3 小时前
【JUC】Future + CompletableFuture详解
java·juc·1024程序员节
君鼎3 小时前
C++通用业务标准库中常用接口函数总结
c++·1024程序员节
杨筱毅3 小时前
【穿越Effective C++】条款5:了解C++默默编写并调用哪些函数——编译器自动生成的秘密
c++·effective c++·1024程序员节
代码不停3 小时前
网络 UDP 和 TCP / IP详细介绍
网络·网络协议·tcp/ip·udp·1024程序员节
Bert丶seven3 小时前
鸿蒙Harmony实战开发教学(No.8)-Hyperlink超链接组件基础到进阶篇
华为·harmonyos·arkts·arkui·1024程序员节·开发教程
虚行3 小时前
1.Go基础知识入门
1024程序员节