C++处理图存储的方式分享

C++处理图存储的方式分享

在C++中处理图的存储方式有多种,这里主要讲解三种最常见和实用的方式:邻接矩阵邻接表关联数组

邻接矩阵

邻接矩阵是图最简单、使用最广泛的存储方式之一,它使用一个二维矩阵表示节点之间的关系。当图中有 n 个节点时,可以用一个 n x n 的矩阵来存储它们之间的关系,矩阵中的每个元素存储两个节点之间的边的信息,如边的权重。

以下是一个用邻接矩阵存储的无向有权图的代码示例:

const int MAXN = 1000;   // 最多节点数
int Graph[MAXN][MAXN];   // 邻接矩阵,可存储节点间权重信息
int n;                   // 图中节点总数

int main(){
    // 初始化图
    memset(Graph, 0, sizeof(Graph)); // 图的全部初始化为零,即无边
    // 显示节点i和节点j之间的权重
    printf("%d\n", Graph[i][j]);
    return 0;
}

邻接表

邻接表用链表方式存储图,对于一个有 n 个节点和 m 条边的图,可以使用一个大小为 n 的数组,每个数组元素存储与其相连的所有节点。相比邻接矩阵,邻接表能够更有效地存储稀疏图。

以下是一个用邻接表存储的有向无权图的代码示例:

const int MAXN = 1000;       // 最多节点数
vector<int> Graph[MAXN];     // 邻接表,存储节点的后继
bool visited[MAXN];          // 图结点是否已经遍历
int n;                       // 图中节点总数

void dfs(int curr_node){     // dfs搜索当前节点
    visited[curr_node] = true;
    for(int i=0; i<Graph[curr_node].size(); i++){
        if(!visited[Graph[curr_node][i]]){
            dfs(Graph[curr_node][i]);
        }
    }
}

int main(){
    // 初始化图中每个节点的后继为空
    for(int i=0; i<=n; i++){
        Graph[i].clear(); 
    }
    // 访问节点i的所有后继
    for(int i=0; i<Graph[i].size(); i++){
        int next_node = Graph[i][j];
        // do something...
    }
    return 0;
}

关联数组

关联数组是一种特殊的数组,使用键-值对存储数据。对于图来说,关联数组可以用来实现一个稀疏的邻接矩阵。可以使用 C++ STL 提供的 map 来实现关联数组。

以下是用 map 存储的有向有权图的代码示例:

const int maxn = 1000;      // 最多节点数
map<int, map<int, int> > Graph;   // 关联数组,存储课件之间的权重
int n;                      // 图中节点总数

int main(){
    // 初始化图中每对节点之间的权重(无穷大)
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++){
            Graph[i][j] = INF;    // 无穷大表示不可达
        }
    }
    // 显示节点i和节点j之间的权重
    printf("%d\n", Graph[i][j]);
    return 0;
}

示例说明:

接下来,我们通过一些示例来说明如何使用上述三种方法来处理图。

邻接矩阵

假设我们有一个无向有权图,有 n 个节点,现在想要找到任意两点之间的路径长度。假设图的邻接矩阵为 Graph[][],以下为代码实现:

for(int i=1; i<=n; i++){
    for(int j=i+1; j<=n; j++){
        printf("path between node %d and %d is: ", i, j);
        if(!Graph[i][j]){
            printf("no path\n");
        }else{
            printf("%d\n", Graph[i][j]);
        }
    }
}

以上代码中,使用两重循环对每个节点对进行遍历,当发现节点对 (i, j) 之间没有路径时,即输出 "no path",否则输出节点对之间的路径长度。

邻接表

现在我们有一个有向无权图,想找到从某个节点开始到另一个节点的所有路径。假设图的邻接表为 Graph[],以下为代码实现:

void dfs(int curr_node, int dest_node, vector<int>& path){
    path.push_back(curr_node);
    if(curr_node == dest_node){
        // 当前为目标节点,则输出路径
        for(int i=0; i<path.size(); i++){
            printf("%d ", path[i]);
        }
        printf("\n");
    }else{
        // 否则继续遍历
        for(int i=0; i<Graph[curr_node].size(); i++){
            dfs(Graph[curr_node][i], dest_node, path);
        }
    }
    path.pop_back();    // 回溯
}

int main(){
    vector<int> path;
    dfs(1, 5, path); // 找从节点1到节点5的所有路径
    return 0;
}

以上代码中,使用深度优先搜索算法递归遍历节点,同时使用一个记录路径的向量 path 来保存路径。当遍历到终点时,输出所有保存在 path 中的节点,从而找到所有路径。

关联数组

现在我们有一个有向有权图,要找到从某个节点开始到其他所有节点的最短路径。假设图的关联数组为 Graph,以下为代码实现:

const int INF = 1e9;  // 假设无穷大为1e9

void Dijkstra(int S){
    priority_queue< pair<int, int>, vector< pair<int, int> >, greater< pair<int, int> > > Q; // 最小堆
    vector<int> dis(n+1, INF);    // 到每个点的距离
    vector<int> pre(n+1, 0);      // 记录到最短路径上每个节点的前一个节点
    dis[S] = 0;
    Q.push(make_pair(0, S));
    while(!Q.empty()){
        int u = Q.top().second; Q.pop();
        for(map<int, int>::iterator iter = Graph[u].begin(); iter != Graph[u].end(); iter++){
            int v = iter->first;
            int w = iter->second;
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                pre[v] = u;
                Q.push(make_pair(dis[v], v));
            }
        }
    }
}

int main(){
    Dijkstra(1);   // 找节点1到其他所有节点的最短路径
    return 0;
}

以上代码中,使用 Dijkstra 算法遍历以起点 S 开始到所有节点的最短路径,同时记录每个节点的前驱节点。当遍历完成后,输出所有节点到起点的最短路径长度,并输出最短路径的具体路径。

注意,以上的关联数组实现方式使用 STL 的 map 类型,但是在实际使用中,我们可以使用 C++11 的 unordered_map 容器替代 map,以提高代码效率。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++处理图存储的方式分享 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • 利用C语言实现猜数字小游戏

    下面是利用C语言实现猜数字小游戏的完整攻略: 步骤一:程序基本结构的搭建 我们可以先编写一个基本的程序框架,包含头文件、全局变量、函数声明等部分。具体代码如下: #include <stdio.h> #include <stdlib.h> #include <time.h> int secret_number; // 定义…

    C 2023年5月23日
    00
  • 如何通过UltraEdit解析BMP文件内部结构(BMP位图基础)

    下面我将详细讲解如何通过UltraEdit解析BMP文件内部结构。 准备工作 下载安装UltraEdit 准备一张BMP格式的图片 解析过程 打开UltraEdit软件 打开BMP文件:文件 > 打开 打开文件后,将光标移至字节流数据处,右键点击选择Hex/ASCII菜单,然后点击设置’#’注释符。 在弹出的对话框中,选择无作为注释符,点击确定按钮。 …

    C 2023年5月23日
    00
  • C语言函数语法详解

    针对您提出的问题,我将为您详细讲解C语言函数语法的详细攻略。 什么是函数 函数是C语言中非常重要的程序构建模块,简单来说,函数就是封装了一段可重用的代码,也就是说可以把这段代码当成“黑盒子”,在需要的时候直接调用即可。一个好的函数应该具有以下几个特点: 可重用性:一个好的函数应该是可重用的,可以在程序的多个不同位置调用。 独立性:函数应该尽可能独立,不受函数…

    C 2023年5月23日
    00
  • 逍遥自在学C语言 | 条件控制的正确使用姿势

    前言 在C语言中,有三种条件判断结构:if语句、if-else语句和switch语句。 一、人物简介 第一位闪亮登场,有请今后会一直教我们C语言的老师 —— 自在。 第二位上场的是和我们一起学习的小白程序猿 —— 逍遥。 二、if语句 基本语法 if (条件) { // 代码块1 } 代码示例 #include <stdio.h> int mai…

    C 2023年5月9日
    00
  • C/C++中可变参数的用法详细解析

    C/C++ 中可变参数的用法详细解析 在 C/C++ 中,我们可以利用可变参数来实现函数的灵活性和通用性。 在本文中,我们将深入了解可变参数的定义、使用、示例和最佳实践。 什么是可变参数? 可变参数是指函数参数的数量和类型是可变的。通常情况下,我们定义函数时需要指定固定数量和类型的参数,例如: int sum(int a, int b, int c) { r…

    C 2023年5月24日
    00
  • Java 异常详解

    Java异常详解 什么是异常 异常(Exception)是指程序在运行期间发生了意外或异常的事件。Java 中的异常是一种对象,它表示在执行过程中发生的错误,异常可以是 checked 或 unchecked。 Checked 异常需要在代码中显式地处理,否则会在编译期产生错误。 Unchecked 异常不需要在代码中显式地处理,编译器不会提示错误,程序在运…

    C 2023年5月23日
    00
  • C语言实现简单计算器功能(2)

    当我们实现一个简单的计算器功能时,需要考虑以下几个方面: 用户输入的合法性检查 进行算术运算的函数实现 错误处理和提示信息输出 第一步,我们需要先获取用户输入的表达式,并对其进行合法性检查。用户输入的表达式应该是一个合法的算术表达式,不能含有非法字符,比如字母等。我们可以使用正则表达式来判断用户输入的内容是否合法。 示例1: #include <reg…

    C 2023年5月23日
    00
  • 全局变量与局部变量在内存中的区别详细解析

    全局变量与局部变量是程序设计中常用的两种变量类型。它们在内存中存储的位置和访问方式都有很大的不同。本文将详细解析它们的区别,并通过两条示例,说明它们在内存中的不同存储方式。 全局变量 全局变量是定义在程序的主体之外的变量,可以被程序的任意部分访问。在C语言中,通过在函数外部定义变量可以创建全局变量。 全局变量的存储位置是在程序的静态数据区中。在程序启动时,就…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部