数据结构 图(Graph)

数据结构 图(Graph)

1. 图介绍

  • 在数据结构中, Graph是一种非常重要的数据结构,用于表示不同的对象(主要是节点)之间的关系。

  • 图由节点V(顶点)(Vertex)和边EEdge)组成,即有Graph={∑V+∑E},节点表示图中的元素,而边表示节点之间的关系。图可以用于建模各种实际问题,如社交网络中的用户关系、计算机网络中的路由、地图中的道路网络等。

  • 图可以分为有向图DAG和无向图G两种类型。在有向图中,边有方向,表示从一个节点到另一个节点的单向关系;而在无向图中,边没有方向,表示节点之间的双向关系。

  • 图还可以根据边的权重分为带权图和无权图。带权图中,每条边都有一个相关联的权重或成本WWeight),此时Graph={∑V+∑E+∑W},而在无权图中,所有边的权重都是相同的(1或者常数),或者没有权重。

  • 图的常见操作包括添加节点、添加边、删除节点、删除边、查找节点、查找边等。图的遍历算法包括深度优先搜索(DFS)和广度优先搜索(BFS),用于遍历图中的所有节点。

  • 入度和出度,入度表示有多少条边指向特定的节点,而出度表示从特定的节点出发一共有多少边指向其他节点。

2. 图分类

2.1 按有无方向划分

2.1.1 有向图
  • 有向图无环(DAG)的性质:
  • DAG是一个有向图,其中边有方向,表示从一个节点到另一个节点的单向关系。
  • DAG 不包含任何环路,即不存在从某个节点出发经过若干条边回到该节点的路径。因此,DAG 的结构是一个有向无环的图。
  • DAG 可以用来表示一些具有前后关系的任务或事件,如任务调度、依赖关系等。例如,编译过程中的源文件依赖关系就可以用 DAG 来表示。
  • 由于没有环路,DAG 中不存在循环依赖的问题,因此可以进行拓扑排序等操作。
  • 有向图
2.1.2 无向图
  • 无向图(G)的性质:
  • 无向图中的边没有方向,表示节点之间的双向关系,即任意两个节点之间都可以互相到达。
  • 无向图可以有环路,即存在一条路径可以从某个节点出发经过若干条边回到该节点。
  • 无向图通常用来表示不考虑方向的关系,如社交网络中的好友关系、交通网络中的道路连接等。
  • 无向图中的边是对称的,即如果节点 A 与节点 B 之间有一条边,那么节点 B 与节点 A 之间也有一条边。
2.1.3 图的存储方式

图主要以邻接表邻接矩阵作为存储媒介进行存储。

2.1.3.1 邻接表
  • 邻接表是一种基于链表的数据结构,用于表示图的结构。
    对于每个节点,邻接表存储其相邻节点的列表。通常是通过数组链表的组合来实现,其中数组的索引表示节点,而每个数组元素是一个链表,存储与该节点相邻的节点。
    邻接表适用于稀疏图,即节点数相对边数较少的图,因为它只存储实际存在的边,节省了空间。

  • 链表法

  • 数组法

2.1.3.2 邻接矩阵
  • 邻接矩阵是一个二维数组,用于表示图的结构。
    对于具有 n 个节点的图,邻接矩阵是一个 n × n 的矩阵,其中行和列分别表示节点,矩阵的元素表示节点之间是否有边相连。
    如果图是有向图,邻接矩阵中的元素可以是 01,表示没有边或有边;如果图是带权图,邻接矩阵中的元素可以是实际的权重值W
    邻接矩阵适用于稠密图,即节点数和边数相对较多的图,因为它提供了 O(1) 时间复杂度的边查找操作,但是占用了更多的空间。
  • 邻接矩阵法

2.2 按权重划分

2.2.1 带权图
  • 带权图是指图中的边携带有权重或者相关的数值信息。

  • 在带权图中,每条边都与一个权重值相关联,表示边的强度、长度、成本等。

  • 带权图常用于建模一些实际问题,如最短路径问题、列车调度问题、哥尼斯堡的“七桥问题”等。

  • 带权图也可以是无向图或有向图,无向带权图表示节点之间的双向关系并携带权重,有向带权图表示单向关系并携带权重。

    七桥问题

2.2.2 无权图
  • 不带权图是指图中的边没有权重或者没有相关的数值信息。

  • 在不带权图中,边只表示节点之间的连接关系,而不包含额外的信息。

  • 无向图有向图都可以是不带权图,其中无向图的边表示节点之间的双向关系,而有向图的边表示单向关系。

3. 图常见操作

3.1 添加节点(顶点)

用于向图中添加新的节点。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 向图中添加新节点
void addVertex(Graph *graph) {
    if (graph->numVertices < MAX_VERTICES) {
        // 新节点的索引即为当前节点数量
        graph->numVertices++;
    } else {
        printf("图中节点数量已达到最大值,无法添加新节点。\n");
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加三个节点
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);

    printf("图中当前节点数量:%d\n", graph.numVertices);

    return 0;
}

3.2 删除节点

从图中删除指定的节点,以及与该节点相关联的边。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 向图中添加新节点
void addVertex(Graph *graph) {
    if (graph->numVertices < MAX_VERTICES) {
        // 新节点的索引即为当前节点数量
        graph->numVertices++;
    } else {
        printf("图中节点数量已达到最大值,无法添加新节点。\n");
    }
}

// 从图中删除节点及其相关联的边
void removeVertex(Graph *graph, int vertex) {
    if (vertex >= 0 && vertex < graph->numVertices) {
        int i, j;

        // 从邻接矩阵中删除相关联的边
        for (i = 0; i < graph->numVertices; i++) {
            graph->adjMatrix[vertex][i] = 0; // 删除与该节点相关联的出边
            graph->adjMatrix[i][vertex] = 0; // 删除与该节点相关联的入边
        }

        // 将该节点从图中删除
        for (i = vertex; i < graph->numVertices - 1; i++) {
            for (j = 0; j < graph->numVertices; j++) {
                graph->adjMatrix[j][i] = graph->adjMatrix[j][i + 1]; // 将后面的节点向前移动
            }
        }
        for (i = vertex; i < graph->numVertices - 1; i++) {
            for (j = 0; j < graph->numVertices; j++) {
                graph->adjMatrix[i][j] = graph->adjMatrix[i + 1][j]; // 将后面的节点向前移动
            }
        }

        graph->numVertices--; // 更新节点数量
    } else {
        printf("要删除的节点不存在。\n");
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);

    // 添加一些边
    graph.adjMatrix[0][1] = 1; // 添加从节点0到节点1的边
    graph.adjMatrix[1][2] = 1; // 添加从节点1到节点2的边
    graph.adjMatrix[2][3] = 1; // 添加从节点2到节点3的边

    printf("删除前,图中当前节点数量:%d\n", graph.numVertices);

    // 删除节点2及其相关联的边
    removeVertex(&graph, 2);

    printf("删除后,图中当前节点数量:%d\n", graph.numVertices);

    return 0;
}

3.3 添加边

在图中添加一条边,连接两个节点。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 向图中添加新节点
void addVertex(Graph *graph) {
    if (graph->numVertices < MAX_VERTICES) {
        // 新节点的索引即为当前节点数量
        graph->numVertices++;
    } else {
        printf("图中节点数量已达到最大值,无法添加新节点。\n");
    }
}

// 在图中添加一条边,连接两个节点
void addEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 添加边将邻接矩阵中对应位置设为1
        graph->adjMatrix[source][destination] = 1;
        // 如果是无向图,还需添加反向边
        graph->adjMatrix[destination][source] = 1;

        // 更新边的数量
        graph->numEdges++;
    } else {
        printf("节点索引无效,无法添加边。\n");
    }
}

// 从图中删除一条边
void removeEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 删除边将邻接矩阵中对应位置设为0
        graph->adjMatrix[source][destination] = 0;
        // 如果是无向图,还需删除反向边
        graph->adjMatrix[destination][source] = 0;

        // 更新边的数量
        graph->numEdges--;
    } else {
        printf("节点索引无效,无法删除边。\n");
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);

    printf("添加边前,图中当前边的数量:%d\n", graph.numEdges);

    // 添加边连接节点0和节点1
    addEdge(&graph, 0, 1);
    addEdge(&graph, 1, 2);

    printf("添加边后,图中当前边的数量:%d\n", graph.numEdges);

    return 0;
}

3.4 删除边

从图中删除指定的边。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 向图中添加新节点
void addVertex(Graph *graph) {
    if (graph->numVertices < MAX_VERTICES) {
        // 新节点的索引即为当前节点数量
        graph->numVertices++;
    } else {
        printf("图中节点数量已达到最大值,无法添加新节点。\n");
    }
}

// 在图中添加一条边,连接两个节点
void addEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 添加边将邻接矩阵中对应位置设为1
        graph->adjMatrix[source][destination] = 1;
        // 如果是无向图,还需添加反向边
        graph->adjMatrix[destination][source] = 1;
        
        // 更新边的数量
        graph->numEdges++;
    } else {
        printf("节点索引无效,无法添加边。\n");
    }
}

// 从图中删除一条边
void removeEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 删除边将邻接矩阵中对应位置设为0
        graph->adjMatrix[source][destination] = 0;
        // 如果是无向图,还需删除反向边
        graph->adjMatrix[destination][source] = 0;
        
        // 更新边的数量
        graph->numEdges--;
    } else {
        printf("节点索引无效,无法删除边。\n");
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);

    // 添加边连接节点0和节点1
    addEdge(&graph, 0, 1);
    addEdge(&graph, 1, 2);

    printf("图中当前边的数量:%d\n", graph.numEdges);

    // 删除连接节点0和节点1的边
    removeEdge(&graph, 0, 1);

    printf("删除边后,图中当前边的数量:%d\n", graph.numEdges);

    return 0;
}

3.5 查找节点

在图中查找指定的节点。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 向图中添加新节点
void addVertex(Graph *graph) {
    if (graph->numVertices < MAX_VERTICES) {
        // 新节点的索引即为当前节点数量
        graph->numVertices++;
    } else {
        printf("图中节点数量已达到最大值,无法添加新节点。\n");
    }
}

// 在图中添加一条边,连接两个节点
void addEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 添加边将邻接矩阵中对应位置设为1
        graph->adjMatrix[source][destination] = 1;
        // 如果是无向图,还需添加反向边
        graph->adjMatrix[destination][source] = 1;
        
        // 更新边的数量
        graph->numEdges++;
    } else {
        printf("节点索引无效,无法添加边。\n");
    }
}

// 在图中查找指定的节点
bool findVertex(Graph *graph, int vertex) {
    if (vertex >= 0 && vertex < graph->numVertices) {
        printf("节点 %d 存在于图中。\n", vertex);
        return true;
    } else {
        printf("节点 %d 不存在于图中。\n", vertex);
        return false;
    }
}

// 从图中删除一条边
void removeEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 删除边将邻接矩阵中对应位置设为0
        graph->adjMatrix[source][destination] = 0;
        // 如果是无向图,还需删除反向边
        graph->adjMatrix[destination][source] = 0;
        
        // 更新边的数量
        graph->numEdges--;
    } else {
        printf("节点索引无效,无法删除边。\n");
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);

    // 添加边连接节点0和节点1
    addEdge(&graph, 0, 1);
    addEdge(&graph, 1, 2);

    int targetVertex = 3;
    findVertex(&graph, targetVertex);

    return 0;
}

3.6 查找边

在图中查找两个节点之间是否存在边。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 向图中添加新节点
void addVertex(Graph *graph) {
    if (graph->numVertices < MAX_VERTICES) {
        // 新节点的索引即为当前节点数量
        graph->numVertices++;
    } else {
        printf("图中节点数量已达到最大值,无法添加新节点。\n");
    }
}

// 在图中添加一条边,连接两个节点
void addEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 添加边将邻接矩阵中对应位置设为1
        graph->adjMatrix[source][destination] = 1;
        // 如果是无向图,还需添加反向边
        graph->adjMatrix[destination][source] = 1;
        
        // 更新边的数量
        graph->numEdges++;
    } else {
        printf("节点索引无效,无法添加边。\n");
    }
}

// 在图中查找指定的节点
bool findVertex(Graph *graph, int vertex) {
    if (vertex >= 0 && vertex < graph->numVertices) {
        printf("节点 %d 存在于图中。\n", vertex);
        return true;
    } else {
        printf("节点 %d 不存在于图中。\n", vertex);
        return false;
    }
}

// 在图中检查两个节点之间是否存在边
bool hasEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        if (graph->adjMatrix[source][destination] == 1) {
            printf("节点 %d 和节点 %d 之间存在边。\n", source, destination);
            return true;
        } else {
            printf("节点 %d 和节点 %d 之间不存在边。\n", source, destination);
            return false;
        }
    } else {
        printf("节点索引无效,无法查找边。\n");
        return false;
    }
}

// 从图中删除一条边
void removeEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 删除边将邻接矩阵中对应位置设为0
        graph->adjMatrix[source][destination] = 0;
        // 如果是无向图,还需删除反向边
        graph->adjMatrix[destination][source] = 0;
        
        // 更新边的数量
        graph->numEdges--;
    } else {
        printf("节点索引无效,无法删除边。\n");
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);

    // 添加边连接节点0和节点1
    addEdge(&graph, 0, 1);
    addEdge(&graph, 1, 2);

    // 检查两个节点之间是否存在边
    int node1 = 0;
    int node2 = 1;
    hasEdge(&graph, node1, node2);

    return 0;
}

3.7 获取节点的邻居(相邻节点)

获取与指定节点直接相连的节点集合。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 向图中添加新节点
void addVertex(Graph *graph) {
    if (graph->numVertices < MAX_VERTICES) {
        // 新节点的索引即为当前节点数量
        graph->numVertices++;
    } else {
        printf("图中节点数量已达到最大值,无法添加新节点。\n");
    }
}

// 在图中添加一条边,连接两个节点
void addEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 添加边将邻接矩阵中对应位置设为1
        graph->adjMatrix[source][destination] = 1;
        // 如果是无向图,还需添加反向边
        graph->adjMatrix[destination][source] = 1;
        
        // 更新边的数量
        graph->numEdges++;
    } else {
        printf("节点索引无效,无法添加边。\n");
    }
}

// 在图中查找指定的节点
bool findVertex(Graph *graph, int vertex) {
    if (vertex >= 0 && vertex < graph->numVertices) {
        printf("节点 %d 存在于图中。\n", vertex);
        return true;
    } else {
        printf("节点 %d 不存在于图中。\n", vertex);
        return false;
    }
}

// 在图中检查两个节点之间是否存在边
bool hasEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        if (graph->adjMatrix[source][destination] == 1) {
            printf("节点 %d 和节点 %d 之间存在边。\n", source, destination);
            return true;
        } else {
            printf("节点 %d 和节点 %d 之间不存在边。\n", source, destination);
            return false;
        }
    } else {
        printf("节点索引无效,无法查找边。\n");
        return false;
    }
}

// 获取与指定节点直接相连的节点集合
void getAdjacentVertices(Graph *graph, int vertex, int adjacentVertices[], int *numAdjacent) {
    if (vertex >= 0 && vertex < graph->numVertices) {
        *numAdjacent = 0;
        for (int i = 0; i < graph->numVertices; i++) {
            if (graph->adjMatrix[vertex][i] == 1) {
                adjacentVertices[*numAdjacent] = i;
                (*numAdjacent)++;
            }
        }
    } else {
        printf("节点索引无效,无法获取相邻节点。\n");
    }
}

// 从图中删除一条边
void removeEdge(Graph *graph, int source, int destination) {
    if (source >= 0 && source < graph->numVertices && destination >= 0 && destination < graph->numVertices) {
        // 删除边将邻接矩阵中对应位置设为0
        graph->adjMatrix[source][destination] = 0;
        // 如果是无向图,还需删除反向边
        graph->adjMatrix[destination][source] = 0;
        
        // 更新边的数量
        graph->numEdges--;
    } else {
        printf("节点索引无效,无法删除边。\n");
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);
    addVertex(&graph);

    // 添加边连接节点0和节点1
    addEdge(&graph, 0, 1);
    addEdge(&graph, 1, 2);

    // 获取与指定节点直接相连的节点集合
    int node = 1;
    int adjacentVertices[MAX_VERTICES];
    int numAdjacent;
    getAdjacentVertices(&graph, node, adjacentVertices, &numAdjacent);

    printf("节点 %d 的相邻节点有:", node);
    for (int i = 0; i < numAdjacent; i++) {
        printf("%d ", adjacentVertices[i]);
    }
    printf("\n");

    return 0;
}

3.8 计算节点的度

统计与节点相连的边的数量,即节点的度。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 统计与节点相连的边的数量,即节点的度
int getDegree(Graph *graph, int vertex) {
    if (vertex >= 0 && vertex < graph->numVertices) {
        int degree = 0;
        for (int i = 0; i < graph->numVertices; i++) {
            if (graph->adjMatrix[vertex][i] == 1) {
                degree++;
            }
        }
        return degree;
    } else {
        printf("节点索引无效,无法获取节点度。\n");
        return -1;
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    graph.numVertices = 4;

    // 添加边连接节点0和节点1
    graph.adjMatrix[0][1] = 1;
    graph.adjMatrix[1][0] = 1;

    // 统计节点的度并展示
    int node = 0;
    int degree = getDegree(&graph, node);
    printf("节点 %d 的度为 %d\n", node, degree);

    return 0;
}

3.9 遍历图

访问图中所有的节点和边,以便对其进行操作或分析。

常见的图遍历算法包括深度优先搜索DFS)和广度优先搜索BFS)。

DFSBFS遍历结果可能会不一样

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 深度优先搜索
void dfs(Graph *graph, int vertex, bool visited[]) {
    visited[vertex] = true;
    printf("访问节点 %d\n", vertex);

    for (int i = 0; i < graph->numVertices; i++) {
        if (graph->adjMatrix[vertex][i] == 1 && !visited[i]) {
            dfs(graph, i, visited);
        }
    }
}

// 广度优先搜索
void bfs(Graph *graph, int start) {
    bool visited[MAX_VERTICES] = {false};
    int queue[MAX_VERTICES];
    int front = 0, rear = 0;

    visited[start] = true;
    printf("访问节点 %d\n", start);
    queue[rear++] = start;

    while (front < rear) {
        int current = queue[front++];
        for (int i = 0; i < graph->numVertices; i++) {
            if (graph->adjMatrix[current][i] == 1 && !visited[i]) {
                visited[i] = true;
                printf("访问节点 %d\n", i);
                queue[rear++] = i;
            }
        }
    }
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    graph.numVertices = 4;

    // 添加边连接节点0和节点1,节点1和节点2
    graph.adjMatrix[0][1] = 1;
    graph.adjMatrix[1][0] = 1;
    graph.adjMatrix[1][2] = 1;
    graph.adjMatrix[2][1] = 1;

    printf("深度优先搜索结果:\n");
    bool visitedDFS[MAX_VERTICES] = {false};
    dfs(&graph, 0, visitedDFS);

    printf("\n广度优先搜索结果:\n");
    bfs(&graph, 0);

    return 0;
}

DFS和BFS遍历图

3.10 检测图的连通性

判断图中是否存在路径连接任意两个节点,以及图中的连通分量数量。

在图论中,一个连通分量是指图中的一个最大子图,其中任意两个顶点都可以通过边相连。换句话说,一个连通分量是一个子图,其中从任意一个顶点出发都可以到达图中的任意其他顶点,而且这个子图不能再扩充,否则就不再是一个连通分量了。

在无向图中,每个连通分量都是一个连通的子图,而且没有两个连通分量有交集。一个无向图可能包含一个或多个连通分量。

在有向图中,连通分量被称为强连通分量(Strongly Connected Component,SCC),它是指一个有向图中的极大子图,其中任意两个顶点都可以互相到达。

可以用DFS 也可以用 BFS进行连通分量数目的统计。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 深度优先搜索,用于判断连通分量数量
void dfs(Graph *graph, int vertex, bool visited[]) {
    visited[vertex] = true;
    for (int i = 0; i < graph->numVertices; i++) {
        if (graph->adjMatrix[vertex][i] == 1 && !visited[i]) {
            dfs(graph, i, visited);
        }
    }
}

// 获取图中的连通分量数量
int getConnectedComponents(Graph *graph) {
    bool visited[MAX_VERTICES] = {false};
    int numComponents = 0;

    for (int i = 0; i < graph->numVertices; i++) {
        if (!visited[i]) {
            dfs(graph, i, visited);
            numComponents++;
        }
    }

    return numComponents;
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    graph.numVertices = 4;

    // 添加边连接节点0和节点1,节点1和节点2
    graph.adjMatrix[0][1] = 1;
    graph.adjMatrix[1][0] = 1;
    graph.adjMatrix[1][2] = 1;
    graph.adjMatrix[2][1] = 1;

    int numComponents = getConnectedComponents(&graph);
    printf("图中的连通分量数量:%d\n", numComponents);

    return 0;
}

3.11 计算最短路径

计算两个节点之间的最短路径,以及最短路径的长度。

可以解决实际生活中真实存在的问题。Dijkstra迪杰斯特拉)算法主要用于计算最短路径。

Dijkstra迪杰斯特拉)算法的基本思想是通过不断地“释放”节点来逐步确定最短路径的长度。具体来说,它维护一个距离数组,记录了从源节点到每个节点的最短距离。初始时,源节点的距离为0,其他节点的距离为无穷大INF。然后,它通过选择未被释放的节点中距离最小的节点来进行释放操作,即更新与该节点相邻的节点的距离。这样,每次选择距离最小的节点来释放,直到所有节点都被释放,最终得到了从源节点到其他所有节点的最短路径长度。

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

#define MAX_VERTICES 100 // 假设最大节点数量为100
#define INF INT_MAX      // 无穷大值,表示不可达

// 图的结构体定义
typedef struct {
    int numVertices; // 节点数量
    int numEdges;    // 边的数量
    int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图的连接关系
} Graph;

// 初始化图
void initializeGraph(Graph *graph) {
    int i, j;
    graph->numVertices = 0;
    graph->numEdges = 0;
    for (i = 0; i < MAX_VERTICES; i++) {
        for (j = 0; j < MAX_VERTICES; j++) {
            graph->adjMatrix[i][j] = 0; // 初始化邻接矩阵,表示节点间没有边相连
        }
    }
}

// 计算两个节点之间的最短路径长度
int shortestPathLength(Graph *graph, int source, int destination) {
    int distance[MAX_VERTICES]; // 存储从起始节点到每个节点的最短距离
    bool visited[MAX_VERTICES] = {false}; // 记录节点是否被访问过
    int i, j;

    // 初始化距离数组
    for (i = 0; i < graph->numVertices; i++) {
        distance[i] = INF;
    }

    // 起始节点到自身的距离为0
    distance[source] = 0;

    // 寻找最短路径
    for (i = 0; i < graph->numVertices - 1; i++) {
        int minDistance = INF;
        int minIndex = -1;

        // 选择当前未访问节点中距离起始节点最近的节点
        for (j = 0; j < graph->numVertices; j++) {
            if (!visited[j] && distance[j] < minDistance) {
                minDistance = distance[j];
                minIndex = j;
            }
        }

        // 将选定的节点标记为已访问
        visited[minIndex] = true;

        // 更新与选定节点相连的节点的距离
        for (j = 0; j < graph->numVertices; j++) {
            if (!visited[j] && graph->adjMatrix[minIndex][j] && distance[minIndex] + graph->adjMatrix[minIndex][j] < distance[j]) {
                distance[j] = distance[minIndex] + graph->adjMatrix[minIndex][j];
            }
        }
    }

    return distance[destination];
}

int main() {
    Graph graph;
    initializeGraph(&graph);

    // 添加四个节点
    graph.numVertices = 4;

    // 添加边连接节点0和节点1,节点1和节点2
    graph.adjMatrix[0][1] = 1;
    graph.adjMatrix[1][0] = 1;
    graph.adjMatrix[1][2] = 1;
    graph.adjMatrix[2][1] = 1;

    // 计算两个节点之间的最短路径长度并展示
    int source = 0;
    int destination = 2;
    int shortestLength = shortestPathLength(&graph, source, destination);

    if (shortestLength == INF) {
        printf("节点 %d 到节点 %d 之间不存在路径。\n", source, destination);
    } else {
        printf("节点 %d 到节点 %d 之间的最短路径长度为 %d。\n", source, destination, shortestLength);
    }

    return 0;
}

迪杰斯特拉算法

4. 特殊图

特殊图主要有以下类别:

  • 完全图(Complete Graph):在完全图中,每一对不同的节点都有一条边连接。一个完全图有 n(n−1)/2条边,其中 n 是节点的数量。
  • 空图(Null Graph):空图是一个没有任何边的图,只包含节点。
  • 有环图(Cycle Graph):环图是一个简单环上的图,即所有节点连接成一个环,每个节点与其相邻的两个节点相连。
  • 1709367342008.jpg
  • 树(Tree):树是一种无环且连通的无向图,其中任意两个节点之间有且仅有一条简单路径。树通常用于表示层次结构或是搜索树等数据结构。

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值