【数据结构:C语言版】008:图(Graph)数据结构详解-概念、类型、实现与应用

1. 图的基本概念

        图是一种非线性数据结构,由顶点(Vertex)的集合和连接这些顶点的边(Edge)的集合组成。formally表示为G = (V, E),其中V是顶点集合,E是边集合。

        图可以用来表示很多现实世界中的关系,比如社交网络、地图路线、网络拓扑等。

1.1 基本术语

  • 顶点(Vertex):图中的基本单元,也称为节点。
  • 边(Edge):连接两个顶点的线段。
  • 度(Degree):与某个顶点相连的边的数量。
  • 路径(Path):从一个顶点到另一个顶点的顶点序列。
  • 环(Cycle):起点和终点相同的路径。

2. 图的类型

2.1 有向图与无向图

  • 无向图:边没有方向,$(v,w)$和$(w,v)$表示同一条边。
  • 有向图:边有方向,$(v,w)$和$(w,v)$是不同的边。

2.2 加权图与非加权图

  • 非加权图:边没有与之关联的权值。
  • 加权图:每条边都有一个权值,表示某种成本或距离。

2.3 其他类型

  • 完全图:每个顶点都与其他所有顶点相连。
  • 连通图:任意两个顶点之间都存在路径。
  • 二分图:顶点可以分为两个不相交的集合,每条边连接的两个顶点分别属于这两个集合。

3. 图的表示方法

3.1 邻接矩阵

使用一个二维数组来表示图,如果顶点i和j之间有边,则matrix[i][j] = 1(或权值),否则为0。

#define MAX_VERTICES 100

int adjMatrix[MAX_VERTICES][MAX_VERTICES];

// 初始化图
void initGraph(int vertices) {
    for (int i = 0; i < vertices; i++)
        for (int j = 0; j < vertices; j++)
            adjMatrix[i][j] = 0;
}

// 添加边
void addEdge(int start, int end) {
    adjMatrix[start][end] = 1;
    adjMatrix[end][start] = 1;  // 对于无向图
}

// 打印图
void printGraph(int vertices) {
    for (int i = 0; i < vertices; i++) {
        for (int j = 0; j < vertices; j++)
            printf("%d ", adjMatrix[i][j]);
        printf("\n");
    }
}

int main() {
    int V = 5;
    initGraph(V);
    addEdge(0, 1);
    addEdge(0, 4);
    addEdge(1, 2);
    addEdge(1, 3);
    addEdge(1, 4);
    addEdge(2, 3);
    addEdge(3, 4);
    
    printf("邻接矩阵表示:\n");
    printGraph(V);
    return 0;
}

输出:

邻接矩阵表示:
0 1 0 0 1 
1 0 1 1 1 
0 1 0 1 0 
0 1 1 0 1 
1 1 0 1 0

3.2 邻接表

对每个顶点,维护一个链表,存储与其相邻的顶点。

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int vertex;
    struct Node* next;
};

struct Graph {
    int numVertices;
    struct Node** adjLists;
};

struct Node* createNode(int v) {
    struct Node* newNode = malloc(sizeof(struct Node));
    newNode->vertex = v;
    newNode->next = NULL;
    return newNode;
}

struct Graph* createGraph(int vertices) {
    struct Graph* graph = malloc(sizeof(struct Graph));
    graph->numVertices = vertices;
    graph->adjLists = malloc(vertices * sizeof(struct Node*));
    
    for (int i = 0; i < vertices; i++)
        graph->adjLists[i] = NULL;
    
    return graph;
}

void addEdge(struct Graph* graph, int src, int dest) {
    struct Node* newNode = createNode(dest);
    newNode->next = graph->adjLists[src];
    graph->adjLists[src] = newNode;
    
    newNode = createNode(src);
    newNode->next = graph->adjLists[dest];
    graph->adjLists[dest] = newNode;
}

void printGraph(struct Graph* graph) {
    for (int v = 0; v < graph->numVertices; v++) {
        struct Node* temp = graph->adjLists[v];
        printf("\n顶点 %d 的邻接表:\n ", v);
        while (temp) {
            printf("%d -> ", temp->vertex);
            temp = temp->next;
        }
        printf("\n");
    }
}

int main() {
    struct Graph* graph = createGraph(5);
    addEdge(graph, 0, 1);
    addEdge(graph, 0, 4);
    addEdge(graph, 1, 2);
    addEdge(graph, 1, 3);
    addEdge(graph, 1, 4);
    addEdge(graph, 2, 3);
    addEdge(graph, 3, 4);
    
    printGraph(graph);
    
    return 0;
}

输出:

顶点 0 的邻接表:
 4 -> 1 -> 

顶点 1 的邻接表:
 4 -> 3 -> 2 -> 0 -> 

顶点 2 的邻接表:
 3 -> 1 -> 

顶点 3 的邻接表:
 4 -> 2 -> 1 -> 

顶点 4 的邻接表:
 3 -> 1 -> 0 ->

4. 图的基本操作

4.1 深度优先搜索(DFS)

DFS从一个顶点开始,尽可能深地探索一条路径,然后回溯。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_VERTICES 100

bool visited[MAX_VERTICES];
int adjMatrix[MAX_VERTICES][MAX_VERTICES];

void dfs(int v, int vertices) {
    visited[v] = true;
    printf("%d ", v);

    for (int i = 0; i < vertices; i++) {
        if (adjMatrix[v][i] == 1 && !visited[i]) {
            dfs(i, vertices);
        }
    }
}

int main() {
    int V = 5;
    // ... 初始化图和添加边的代码 ...

    printf("DFS遍历结果: ");
    dfs(0, V);
    return 0;
}

4.2 广度优先搜索(BFS)

BFS从一个顶点开始,先访问所有邻接顶点,然后再访问下一层顶点。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_VERTICES 100

bool visited[MAX_VERTICES];
int adjMatrix[MAX_VERTICES][MAX_VERTICES];
int queue[MAX_VERTICES];
int front = -1, rear = -1;

void enqueue(int v) {
    if (rear == MAX_VERTICES - 1) return;
    if (front == -1) front = 0;
    queue[++rear] = v;
}

int dequeue() {
    if (front == -1) return -1;
    int item = queue[front];
    front++;
    if (front > rear) front = rear = -1;
    return item;
}

void bfs(int start, int vertices) {
    visited[start] = true;
    printf("%d ", start);
    enqueue(start);

    while (front != -1) {
        int v = dequeue();
        for (int i = 0; i < vertices; i++) {
            if (adjMatrix[v][i] == 1 && !visited[i]) {
                visited[i] = true;
                printf("%d ", i);
                enqueue(i);
            }
        }
    }
}

int main() {
    int V = 5;
    // ... 初始化图和添加边的代码 ...

    printf("BFS遍历结果: ");
    bfs(0, V);
    return 0;
}

5. 图的应用

  1. 社交网络分析:使用图来表示人与人之间的关系,分析社交网络的结构。
  2. 路径规划:在地图应用中使用图来表示道路网络,计算最短路径。
  3. 网络拓扑:在计算机网络中使用图来表示网络结构,优化网络性能。
  4. 推荐系统:使用图来表示用户和商品之间的关系,进行个性化推荐。
  5. 生物信息学:用图来表示蛋白质之间的相互作用网络。
  6. 编译器优化:在编译器中使用图来表示程序的控制流,进行代码优化。
  7. 资源分配:在操作系统中使用图来表示资源依赖关系,避免死锁。

6. 高级图算法

  1. 最短路径算法:Dijkstra算法、Floyd-Warshall算法
  2. 最小生成树:Prim算法、Kruskal算法
  3. 强连通分量:Kosaraju算法、Tarjan算法
  4. 网络流:Ford-Fulkerson算法、Edmonds-Karp算法
  5. 图着色:回溯算法、贪心算法

7. 总结

        图是一种强大而灵活的数据结构,能够表示各种复杂的关系和网络。掌握图的基本概念、表示方法和常用算法对于解决许多实际问题至关重要。随着大数据和人工智能的发展,图在数据分析、机器学习等领域的应用也越来越广泛。

        深入理解图数据结构不仅能帮助我们更好地建模现实世界的问题,还能提高我们的算法设计和问题解决能力。在实际应用中,选择合适的图表示方法和算法,对于提高程序的效率和可扩展性都有重要影响。

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值