C++ 图的保姆级讲解

C++ 图的保姆级讲解

一、前言

在 C++ 编程中,图是一种用于描述对象之间复杂关系的重要数据结构。它由顶点和边组成,边可以是有向的或者无向的。

二、图的基本概念

  1. 顶点(Vertex):图中的节点,表示对象或实体。
  2. 边(Edge):连接顶点的线段,表示顶点之间的关系。
    • 有向边:具有明确的起点和终点。
    • 无向边:没有明确的起点和终点,两个顶点之间的关系是相互的。

三、图的表示方式

  1. 邻接矩阵
    • 原理:使用一个二维数组来表示顶点之间的连接关系。
    • 优点:查询某个顶点与其他顶点的连接情况非常快速。
    • 缺点:空间复杂度较高,对于稀疏图(边的数量相对较少的图)会浪费大量空间。
    • 示例:
const int MAX_VERTICES = 100;
int adjacencyMatrix[MAX_VERTICES][MAX_VERTICES];  // 假设最多有 100 个顶点

// 初始化邻接矩阵
for (int i = 0; i < MAX_VERTICES; ++i) {
    for (int j = 0; j < MAX_VERTICES; ++j) {
        adjacencyMatrix[i][j] = 0;  // 初始化为 0,表示没有边连接
    }
}

// 假设添加一条从顶点 1 到顶点 2 的边
adjacencyMatrix[1][2] = 1;
  1. 邻接表
    • 原理:为每个顶点创建一个链表,链表中存储与该顶点相邻的顶点。
    • 优点:节省空间,尤其对于稀疏图。
    • 缺点:查询特定顶点的相邻顶点相对复杂。
    • 示例:
struct Node {
    int vertex;
    Node* next;
};

class Graph {
private:
    int numVertices;
    Node** adjacencyList;

public:
    Graph(int numVertices) {
        this->numVertices = numVertices;
        adjacencyList = new Node*[numVertices];

        for (int i = 0; i < numVertices; ++i) {
            adjacencyList[i] = NULL;
        }
    }

    void addEdge(int source, int destination) {
        Node* newNode = new Node;
        newNode->vertex = destination;
        newNode->next = adjacencyList[source];
        adjacencyList[source] = newNode;

        // 如果是无向图,还需要添加反向的边
        newNode = new Node;
        newNode->vertex = source;
        newNode->next = adjacencyList[destination];
        adjacencyList[destination] = newNode;
    }

    // 其他相关操作
};

  1. 链式前向星
  • 原理:使用数组存储边,通过指针将边连接起来。
  • 优点:空间效率较高,同时能较方便地进行遍历和操作。
  • 示例:
const int MAX_EDGES = 1000;  // 假设最大边数

struct Edge {
    int from, to, next;
};

Edge edges[MAX_EDGES];
int head[MAX_VERTICES];  // 记录每个顶点的第一条边在 edges 数组中的索引
int edgeCount = 0;

// 添加边的函数
void addEdge(int from, int to) {
    edges[edgeCount].from = from;
    edges[edgeCount].to = to;
    edges[edgeCount].next = head[from];
    head[from] = edgeCount++;
}

// 遍历邻接边
for (int i = head[vertex]; i!= -1; i = edges[i].next) {
    int neighbor = edges[i].to;
    // 处理邻接顶点
}

四、图的遍历
深度优先搜索(Depth-First Search,DFS)
原理:从起始顶点开始,沿着一条路径尽可能深地访问顶点,直到无法继续,然后回溯。
特点:

  • 适合探索图的深度和复杂的分支结构。
  • 可能会陷入过深的路径,导致在某些情况下效率较低。
  • 常用于解决与路径搜索、连通性判断等问题。
    示例:
bool visited[MAX_VERTICES];  // 全局数组用于标记顶点是否已访问

void dfs(int vertex, Graph graph) {
    visited[vertex] = true;
    // 处理当前顶点

    Node* current = graph.adjacencyList[vertex];
    while (current!= NULL) {
        if (!visited[current->vertex]) {
            dfs(current->vertex, graph);
        }
        current = current->next;
    }
}

广度优先搜索(Breadth-First Search,BFS)
原理:从起始顶点开始,逐层地访问相邻顶点,先访问距离起始顶点近的顶点。
特点:

  • 适合寻找最短路径或最小步数问题。
  • 从初始点扩散,不会像深搜一样陷入过深的情况。
  • 常用于解决与最短距离、层次遍历等相关的问题。
    示例:
#include <queue>

void bfs(int startVertex, Graph graph) {
    bool visited[MAX_VERTICES] = {false};
    queue<int> queue;

    visited[startVertex] = true;
    queue.push(startVertex);

    while (!queue.empty()) {
        int currentVertex = queue.front();
        queue.pop();
        // 处理当前顶点

        Node* current = graph.adjacencyList[currentVertex];
        while (current!= NULL) {
            if (!visited[current->vertex]) {
                visited[current->vertex] = true;
                queue.push(current->vertex);
            }
            current = current->next;
        }
    }
}

作者制作不易,点个赞吧(^▽^),把作品的热度顶顶!

© 2024 本内容版权归梅耀元所有,未经授权请勿转载。

7 个赞

链式前向星呢?

1 个赞

我来补

2 个赞

补充一下广搜:适合找最小值,从初始点扩散,不会像深搜一样陷入过深的情况等等都可以补充

1 个赞

补上了

1 个赞

欧克

2 个赞

邻接表一般直接vector实现的,手写麻烦

1 个赞

是的,大佬说的都对

2 个赞

补了

1 个赞

66

1 个赞