C语言数据结构——图

图(Graph)是一种复杂的数据结构,用于表示对象之间的关系。图在计算机科学和许多实际应用中具有广泛的应用,如社交网络分析、路径规划、网络路由、图像处理等。本文将详细介绍如何使用C语言实现图,包括基本概念、图的表示方法、基本操作、常用算法(如深度优先搜索和广度优先搜索)、示例代码、优缺点及其应用场景。

图的基本概念

定义

图由顶点(Vertex)和边(Edge)组成,用于表示对象及其相互关系。图可以分为多种类型,主要包括:

  • 有向图(Directed Graph):边具有方向,从一个顶点指向另一个顶点。

  • 无向图(Undirected Graph):边没有方向,表示两个顶点之间的双向关系。

  • 加权图(Weighted Graph):边具有权重,表示连接顶点之间的成本、距离或其他度量。

  • 无权图(Unweighted Graph):边没有权重,或者所有边具有相同的权重。

应用场景

  1. 社交网络:表示用户(顶点)之间的好友关系(边)。

  2. 地图和路径规划:表示地点(顶点)和道路(边),用于计算最短路径。

  3. 网络路由:表示网络设备(顶点)和连接(边),用于数据包传输。

  4. 任务调度和依赖关系:表示任务(顶点)及其依赖关系(边)。

  5. 编译器设计:表示语法规则(顶点)之间的关系(边)。

图的表示方法

在C语言中,图主要有两种常见的表示方法:

  1. 邻接矩阵(Adjacency Matrix)

  2. 邻接表(Adjacency List)

1. 邻接矩阵

邻接矩阵是一种二维数组,用于表示图中顶点之间的连接关系。对于一个包含V个顶点的图,邻接矩阵是一个V x V的矩阵,其中matrix[i][j]表示顶点i和顶点j之间是否存在边(及其权重)。

优点
  • 查询边是否存在:时间复杂度为O(1)。

  • 实现简单

缺点
  • 空间浪费:对于稀疏图(边数较少的图),邻接矩阵会浪费大量空间。

  • 插入和删除边的时间复杂度高

示例代码
#include <stdio.h>
#include <stdlib.h>

#define MAX_VERTICES 100

typedef struct Graph {
    int numVertices;
    int isDirected;
    int matrix[MAX_VERTICES][MAX_VERTICES];
} Graph;

// 初始化图
void initializeGraph(Graph* g, int numVertices, int isDirected) {
    g->numVertices = numVertices;
    g->isDirected = isDirected;
    for (int i = 0; i < numVertices; i++)
        for (int j = 0; j < numVertices; j++)
            g->matrix[i][j] = 0;
}

// 添加边
void addEdgeMatrix(Graph* g, int src, int dest) {
    if (src >= g->numVertices || dest >= g->numVertices) {
        printf("顶点编号超出范围\n");
        return;
    }
    g->matrix[src][dest] = 1;
    if (!g->isDirected)
        g->matrix[dest][src] = 1;
}

// 删除边
void removeEdgeMatrix(Graph* g, int src, int dest) {
    if (src >= g->numVertices || dest >= g->numVertices) {
        printf("顶点编号超出范围\n");
        return;
    }
    g->matrix[src][dest] = 0;
    if (!g->isDirected)
        g->matrix[dest][src] = 0;
}

// 打印邻接矩阵
void printMatrix(Graph* g) {
    printf("邻接矩阵:\n");
    for (int i = 0; i < g->numVertices; i++) {
        for (int j = 0; j < g->numVertices; j++) {
            printf("%d ", g->matrix[i][j]);
        }
        printf("\n");
    }
}

int main() {
    Graph g;
    initializeGraph(&g, 5, 0); // 创建一个无向图,5个顶点

    addEdgeMatrix(&g, 0, 1);
    addEdgeMatrix(&g, 0, 4);
    addEdgeMatrix(&g, 1, 2);
    addEdgeMatrix(&g, 1, 3);
    addEdgeMatrix(&g, 1, 4);
    addEdgeMatrix(&g, 2, 3);
    addEdgeMatrix(&g, 3, 4);

    printMatrix(&g);

    removeEdgeMatrix(&g, 1, 4);
    printf("删除边(1, 4)后:\n");
    printMatrix(&g);

    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 
删除边(1, 4)后:
0 1 0 0 1 
1 0 1 1 0 
0 1 0 1 0 
0 1 1 0 1 
1 0 0 1 0 

2. 邻接表

邻接表是一种更加节省空间的图表示方法,特别适用于稀疏图。它由一个数组和若干链表组成,数组的每个元素对应一个顶点,链表中存储与该顶点相邻接的所有顶点。

优点
  • 节省空间:仅存储存在的边,适合稀疏图。

  • 遍历邻接顶点更高效

缺点
  • 查询边是否存在的时间复杂度较高(O(V))

  • 实现相对复杂

示例代码
#include <stdio.h>
#include <stdlib.h>

// 定义邻接表的链表节点
typedef struct AdjListNode {
    int dest;
    struct AdjListNode* next;
} AdjListNode;

// 定义邻接表
typedef struct AdjList {
    AdjListNode* head; // 指向链表头部
} AdjList;

// 定义图
typedef struct Graph {
    int numVertices;
    int isDirected;
    AdjList* array;
} Graph;

// 创建一个新的邻接表节点
AdjListNode* createAdjListNode(int dest) {
    AdjListNode* newNode = (AdjListNode*)malloc(sizeof(AdjListNode));
    if (!newNode) {
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->dest = dest;
    newNode->next = NULL;
    return newNode;
}

// 初始化图
Graph* initializeGraph(int numVertices, int isDirected) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    if (!graph) {
        printf("内存分配失败\n");
        exit(1);
    }
    graph->numVertices = numVertices;
    graph->isDirected = isDirected;

    // 创建一个邻接表数组
    graph->array = (AdjList*)malloc(numVertices * sizeof(AdjList));
    if (!graph->array) {
        printf("内存分配失败\n");
        exit(1);
    }

    // 初始化每个邻接表为NULL
    for (int i = 0; i < numVertices; i++)
        graph->array[i].head = NULL;

    return graph;
}

// 添加边
void addEdgeList(Graph* graph, int src, int dest) {
    if (src >= graph->numVertices || dest >= graph->numVertices) {
        printf("顶点编号超出范围\n");
        return;
    }

    // 将dest添加到src的链表
    AdjListNode* newNode = createAdjListNode(dest);
    newNode->next = graph->array[src].head;
    graph->array[src].head = newNode;

    // 如果是无向图,则将src添加到dest的链表
    if (!graph->isDirected) {
        newNode = createAdjListNode(src);
        newNode->next = graph->array[dest].head;
        graph->array[dest].head = newNode;
    }
}

// 删除边
void removeEdgeList(Graph* graph, int src, int dest) {
    if (src >= graph->numVertices || dest >= graph->numVertices) {
        printf("顶点编号超出范围\n");
        return;
    }

    // 从src的链表中删除dest
    AdjListNode* temp = graph->array[src].head;
    AdjListNode* prev = NULL;
    while (temp != NULL && temp->dest != dest) {
        prev = temp;
        temp = temp->next;
    }
    if (temp != NULL) {
        if (prev == NULL)
            graph->array[src].head = temp->next;
        else
            prev->next = temp->next;
        free(temp);
    }

    // 如果是无向图,则从dest的链表中删除src
    if (!graph->isDirected) {
        temp = graph->array[dest].head;
        prev = NULL;
        while (temp != NULL && temp->dest != src) {
            prev = temp;
            temp = temp->next;
        }
        if (temp != NULL) {
            if (prev == NULL)
                graph->array[dest].head = temp->next;
            else
                prev->next = temp->next;
            free(temp);
        }
    }
}

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

int main() {
    int numVertices = 5;
    Graph* graph = initializeGraph(numVertices, 0); // 创建一个无向图

    addEdgeList(graph, 0, 1);
    addEdgeList(graph, 0, 4);
    addEdgeList(graph, 1, 2);
    addEdgeList(graph, 1, 3);
    addEdgeList(graph, 1, 4);
    addEdgeList(graph, 2, 3);
    addEdgeList(graph, 3, 4);

    printAdjList(graph);

    removeEdgeList(graph, 1, 4);
    printf("删除边 (1,4) 后:\n");
    printAdjList(graph);

    return 0;
}
输出结果
顶点 0: -> 4 -> 1
顶点 1: -> 4 -> 3 -> 2 -> 0
顶点 2: -> 3 -> 1
顶点 3: -> 4 -> 2 -> 1
顶点 4: -> 3 -> 1 -> 0
删除边 (1,4) 后:
顶点 0: -> 4 -> 1
顶点 1: -> 3 -> 2 -> 0
顶点 2: -> 3 -> 1
顶点 3: -> 4 -> 2 -> 1
顶点 4: -> 3 -> 0

图的选择:邻接矩阵 vs 邻接表

特性邻接矩阵邻接表
空间复杂度O(V²)O(V + E)
边查询速度O(1)O(V)
边的插入删除O(1) - O(V)(对于稀疏图,O(1)较好)O(1) - O(V)(需要遍历链表)
适用场景稠密图(边数接近最大边数)稀疏图(边数远小于最大边数)
实现复杂度简单相对复杂

图的基本操作

1. 添加顶点

在邻接矩阵表示中,添加顶点通常意味着扩展矩阵的大小。然而,由于C语言中数组大小固定,通常需要提前确定最大顶点数。使用邻接表则更灵活,可以动态添加顶点。

2. 添加边

已在上述表示方法中详细示例。

3. 删除边

已在上述表示方法中详细示例。

4. 遍历图

图的遍历主要有两种常用方式:

  • 深度优先搜索(Depth-First Search, DFS)

  • 广度优先搜索(Breadth-First Search, BFS)

4.1 深度优先搜索(DFS)

DFS是一种递归或使用栈的遍历方法,优先访问深度更大的分支。

递归实现示例(邻接表)
#include <stdio.h>
#include <stdlib.h>

#define MAX_VERTICES 100

// 定义邻接表节点
typedef struct AdjListNode {
    int dest;
    struct AdjListNode* next;
} AdjListNode;

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

// 定义图
typedef struct Graph {
    int numVertices;
    AdjList* array;
} Graph;

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

// 初始化图
Graph* initializeGraph(int numVertices) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    graph->numVertices = numVertices;
    graph->array = (AdjList*)malloc(numVertices * sizeof(AdjList));
    for (int i = 0; i < numVertices; i++)
        graph->array[i].head = NULL;
    return graph;
}

// 添加边
void addEdgeList(Graph* graph, int src, int dest) {
    AdjListNode* newNode = createAdjListNode(dest);
    newNode->next = graph->array[src].head;
    graph->array[src].head = newNode;
}

// 打印图
void printGraph(Graph* graph) {
    for (int v = 0; v < graph->numVertices; v++) {
        AdjListNode* temp = graph->array[v].head;
        printf("顶点 %d:", v);
        while (temp) {
            printf(" -> %d", temp->dest);
            temp = temp->next;
        }
        printf("\n");
    }
}

// DFS递归实现
void DFSUtil(Graph* graph, int v, int visited[]) {
    visited[v] = 1;
    printf("%d ", v);

    AdjListNode* temp = graph->array[v].head;
    while (temp) {
        if (!visited[temp->dest])
            DFSUtil(graph, temp->dest, visited);
        temp = temp->next;
    }
}

// DFS主函数
void DFS(Graph* graph, int startVertex) {
    int* visited = (int*)malloc(graph->numVertices * sizeof(int));
    for (int i = 0; i < graph->numVertices; i++)
        visited[i] = 0;

    printf("DFS遍历(从顶点 %d 开始): ", startVertex);
    DFSUtil(graph, startVertex, visited);
    printf("\n");

    free(visited);
}

int main() {
    int numVertices = 5;
    Graph* graph = initializeGraph(numVertices);

    addEdgeList(graph, 0, 1);
    addEdgeList(graph, 0, 4);
    addEdgeList(graph, 1, 2);
    addEdgeList(graph, 1, 3);
    addEdgeList(graph, 1, 4);
    addEdgeList(graph, 2, 3);
    addEdgeList(graph, 3, 4);

    printGraph(graph);

    DFS(graph, 0); // 从顶点0开始DFS

    return 0;
}
输出结果
顶点 0: -> 4 -> 1
顶点 1: -> 4 -> 3 -> 2
顶点 2: -> 3
顶点 3: -> 4
顶点 4:
DFS遍历(从顶点 0 开始): 0 1 2 3 4 
4.2 广度优先搜索(BFS)

BFS是一种使用队列的遍历方法,先访问离起始顶点较近的节点。

实现示例(邻接表)
#include <stdio.h>
#include <stdlib.h>

// 定义邻接表节点
typedef struct AdjListNode {
    int dest;
    struct AdjListNode* next;
} AdjListNode;

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

// 定义图
typedef struct Graph {
    int numVertices;
    AdjList* array;
} Graph;

// 队列节点
typedef struct QueueNode {
    int data;
    struct QueueNode* next;
} QueueNode;

// 队列
typedef struct Queue {
    QueueNode* front;
    QueueNode* rear;
} Queue;

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

// 初始化图
Graph* initializeGraph(int numVertices) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    graph->numVertices = numVertices;
    graph->array = (AdjList*)malloc(numVertices * sizeof(AdjList));
    for (int i = 0; i < numVertices; i++)
        graph->array[i].head = NULL;
    return graph;
}

// 添加边
void addEdgeList(Graph* graph, int src, int dest) {
    AdjListNode* newNode = createAdjListNode(dest);
    newNode->next = graph->array[src].head;
    graph->array[src].head = newNode;
}

// 打印图
void printGraph(Graph* graph) {
    for (int v = 0; v < graph->numVertices; v++) {
        AdjListNode* temp = graph->array[v].head;
        printf("顶点 %d:", v);
        while (temp) {
            printf(" -> %d", temp->dest);
            temp = temp->next;
        }
        printf("\n");
    }
}

// 创建队列节点
QueueNode* createQueueNode(int data) {
    QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

// 初始化队列
void initializeQueueStruct(Queue* q) {
    q->front = q->rear = NULL;
}

// 检查队列是否为空
int isQueueEmptyStruct(Queue* q) {
    return q->front == NULL;
}

// 入队
void enqueueStruct(Queue* q, int data) {
    QueueNode* temp = createQueueNode(data);
    if (q->rear == NULL) {
        q->front = q->rear = temp;
        return;
    }
    q->rear->next = temp;
    q->rear = temp;
}

// 出队
int dequeueStruct(Queue* q) {
    if (isQueueEmptyStruct(q)) {
        printf("队列为空,无法出队\n");
        exit(1);
    }
    QueueNode* temp = q->front;
    int data = temp->data;
    q->front = q->front->next;
    if (q->front == NULL)
        q->rear = NULL;
    free(temp);
    return data;
}

// BFS实现
void BFS(Graph* graph, int startVertex) {
    int* visited = (int*)malloc(graph->numVertices * sizeof(int));
    for (int i = 0; i < graph->numVertices; i++)
        visited[i] = 0;

    Queue q;
    initializeQueueStruct(&q);

    visited[startVertex] = 1;
    enqueueStruct(&q, startVertex);

    printf("BFS遍历(从顶点 %d 开始): ", startVertex);

    while (!isQueueEmptyStruct(&q)) {
        int currentVertex = dequeueStruct(&q);
        printf("%d ", currentVertex);

        AdjListNode* temp = graph->array[currentVertex].head;
        while (temp) {
            int adjVertex = temp->dest;
            if (!visited[adjVertex]) {
                visited[adjVertex] = 1;
                enqueueStruct(&q, adjVertex);
            }
            temp = temp->next;
        }
    }
    printf("\n");

    free(visited);
}

int main() {
    int numVertices = 5;
    Graph* graph = initializeGraph(numVertices);

    addEdgeList(graph, 0, 1);
    addEdgeList(graph, 0, 4);
    addEdgeList(graph, 1, 2);
    addEdgeList(graph, 1, 3);
    addEdgeList(graph, 1, 4);
    addEdgeList(graph, 2, 3);
    addEdgeList(graph, 3, 4);

    printGraph(graph);

    BFS(graph, 0); // 从顶点0开始BFS

    return 0;
}
输出结果
顶点 0: -> 4 -> 1
顶点 1: -> 4 -> 3 -> 2
顶点 2: -> 3
顶点 3: -> 4
顶点 4:
BFS遍历(从顶点 0 开始): 0 1 4 2 3 

图的常用算法

1. 深度优先搜索(DFS)

DFS用于遍历或搜索图中的节点,从一个起始节点出发,尽可能深地探索每个分支。

实现步骤
  1. 从起始节点开始,访问节点并标记为已访问。

  2. 对每个相邻未访问的节点,递归进行DFS。

应用
  • 查找连通分量

  • 拓扑排序

  • 检测环

2. 广度优先搜索(BFS)

BFS用于遍历或搜索图中的节点,从一个起始节点开始,先访问邻近节点,再逐层向外扩展。

实现步骤
  1. 从起始节点入队并标记为已访问。

  2. 反复从队列中取出节点,访问所有未访问的邻接节点,并将其入队。

应用
  • 查找最短路径(在无权图中)

  • 层次遍历

  • 网络广播

3. 最短路径算法

用于计算图中两个节点之间的最短路径。常见算法包括Dijkstra算法、Bellman-Ford算法、Floyd-Warshall算法等。

示例:Dijkstra算法(未在此详述,可根据需要进一步扩展)

4. 最小生成树(MST)

用于找到图中的子集,使得包含所有顶点且没有环的边的总权重最小。常用算法包括Prim算法和Kruskal算法。

示例:Kruskal算法(未在此详述,可根据需要进一步扩展)

完整示例程序

下面是一个完整的示例程序,展示了如何使用邻接表表示无向图,并执行DFS和BFS遍历操作。

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

// 定义邻接表节点
typedef struct AdjListNode {
    int dest;
    struct AdjListNode* next;
} AdjListNode;

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

// 定义图
typedef struct Graph {
    int numVertices;
    int isDirected;
    AdjList* array;
} Graph;

// 队列节点
typedef struct QueueNode {
    int data;
    struct QueueNode* next;
} QueueNode;

// 队列
typedef struct Queue {
    QueueNode* front;
    QueueNode* rear;
} Queue;

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

// 初始化图
Graph* initializeGraph(int numVertices, int isDirected) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    graph->numVertices = numVertices;
    graph->isDirected = isDirected;
    graph->array = (AdjList*)malloc(numVertices * sizeof(AdjList));

    for (int i = 0; i < numVertices; i++)
        graph->array[i].head = NULL;

    return graph;
}

// 添加边
void addEdgeList(Graph* graph, int src, int dest) {
    if (src >= graph->numVertices || dest >= graph->numVertices) {
        printf("顶点编号超出范围\n");
        return;
    }

    // 添加从src到dest的边
    AdjListNode* newNode = createAdjListNode(dest);
    newNode->next = graph->array[src].head;
    graph->array[src].head = newNode;

    // 如果是无向图,则添加从dest到src的边
    if (!graph->isDirected) {
        newNode = createAdjListNode(src);
        newNode->next = graph->array[dest].head;
        graph->array[dest].head = newNode;
    }
}

// 打印图
void printGraph(Graph* graph) {
    for (int v = 0; v < graph->numVertices; v++) {
        AdjListNode* temp = graph->array[v].head;
        printf("顶点 %d:", v);
        while (temp) {
            printf(" -> %d", temp->dest);
            temp = temp->next;
        }
        printf("\n");
    }
}

// 创建队列节点
QueueNode* createQueueNode(int data) {
    QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

// 初始化队列
void initializeQueueStruct(Queue* q) {
    q->front = q->rear = NULL;
}

// 检查队列是否为空
int isQueueEmptyStruct(Queue* q) {
    return q->front == NULL;
}

// 入队
void enqueueStruct(Queue* q, int data) {
    QueueNode* temp = createQueueNode(data);
    if (q->rear == NULL) {
        q->front = q->rear = temp;
        return;
    }
    q->rear->next = temp;
    q->rear = temp;
}

// 出队
int dequeueStruct(Queue* q) {
    if (isQueueEmptyStruct(q)) {
        printf("队列为空,无法出队\n");
        exit(1);
    }
    QueueNode* temp = q->front;
    int data = temp->data;
    q->front = q->front->next;
    if (q->front == NULL)
        q->rear = NULL;
    free(temp);
    return data;
}

// DFS递归实现
void DFSUtil(Graph* graph, int v, int visited[]) {
    visited[v] = 1;
    printf("%d ", v);

    AdjListNode* temp = graph->array[v].head;
    while (temp) {
        if (!visited[temp->dest])
            DFSUtil(graph, temp->dest, visited);
        temp = temp->next;
    }
}

// DFS主函数
void DFS(Graph* graph, int startVertex) {
    int* visited = (int*)malloc(graph->numVertices * sizeof(int));
    for (int i = 0; i < graph->numVertices; i++)
        visited[i] = 0;

    printf("DFS遍历(从顶点 %d 开始): ", startVertex);
    DFSUtil(graph, startVertex, visited);
    printf("\n");

    free(visited);
}

// BFS实现
void BFS(Graph* graph, int startVertex) {
    int* visited = (int*)malloc(graph->numVertices * sizeof(int));
    for (int i = 0; i < graph->numVertices; i++)
        visited[i] = 0;

    Queue q;
    initializeQueueStruct(&q);

    visited[startVertex] = 1;
    enqueueStruct(&q, startVertex);

    printf("BFS遍历(从顶点 %d 开始): ", startVertex);

    while (!isQueueEmptyStruct(&q)) {
        int currentVertex = dequeueStruct(&q);
        printf("%d ", currentVertex);

        AdjListNode* temp = graph->array[currentVertex].head;
        while (temp) {
            int adjVertex = temp->dest;
            if (!visited[adjVertex]) {
                visited[adjVertex] = 1;
                enqueueStruct(&q, adjVertex);
            }
            temp = temp->next;
        }
    }
    printf("\n");

    free(visited);
}

int main() {
    int numVertices = 5;
    Graph* graph = initializeGraph(numVertices, 0); // 创建一个无向图

    addEdgeList(graph, 0, 1);
    addEdgeList(graph, 0, 4);
    addEdgeList(graph, 1, 2);
    addEdgeList(graph, 1, 3);
    addEdgeList(graph, 1, 4);
    addEdgeList(graph, 2, 3);
    addEdgeList(graph, 3, 4);

    printGraph(graph);

    DFS(graph, 0); // 从顶点0开始DFS
    BFS(graph, 0); // 从顶点0开始BFS

    return 0;
}
输出结果
顶点 0: -> 4 -> 1
顶点 1: -> 4 -> 3 -> 2 -> 0
顶点 2: -> 3 -> 1
顶点 3: -> 4 -> 1 -> 2
顶点 4: -> 1 -> 0 -> 3
DFS遍历(从顶点 0 开始): 0 1 2 3 4 
BFS遍历(从顶点 0 开始): 0 1 4 2 3 

图的优缺点

优点

  1. 灵活性高:可以表示多种关系和结构,如网络、社交关系、依赖关系等。

  2. 强大的表达能力:适用于复杂的关系和多种应用场景。

  3. 多种算法支持:丰富的图算法支持各种计算需求,如最短路径、连通性、匹配等。

缺点

  1. 空间复杂度高:尤其是邻接矩阵表示,对于稀疏图会浪费大量空间。

  2. 实现复杂:特别是对于复杂图算法,代码实现较为复杂。

  3. 难以可视化:大型图的可视化和理解较为困难。

图的变种与扩展

1. 有向无环图(DAG)

DAG是一种有向图,没有环路。广泛应用于任务调度、项目管理、表达式解析等。

2. 权重图

边具有权重,表示连接顶点之间的成本、距离等。常用于路径规划、网络优化等。

3. 多重图

允许多个边连接同一对顶点。常用于表示多种不同类型的关系。

4. 稀疏图与稠密图

  • 稀疏图(Sparse Graph):边数远小于最大边数,通常使用邻接表表示。

  • 稠密图(Dense Graph):边数接近最大边数,通常使用邻接矩阵表示。

图的应用场景

  1. 社交网络分析:表示用户和好友关系,分析社交圈、影响力等。

  2. 地图与导航:表示地理位置和道路网络,计算最短路径。

  3. 网络路由:表示网络节点和连接,优化数据传输路径。

  4. 任务调度与依赖管理:表示任务及其依赖关系,优化执行顺序。

  5. 编译器设计:表示语法规则和依赖关系,进行语法分析。

  6. 推荐系统:表示用户、物品和交互关系,进行个性化推荐。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值