欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > 利用编程思维做题之将无向图的邻接矩阵转换为邻接表

利用编程思维做题之将无向图的邻接矩阵转换为邻接表

2024/10/22 22:40:29 来源:https://blog.csdn.net/qq_39178993/article/details/142881642  浏览:    关键词:利用编程思维做题之将无向图的邻接矩阵转换为邻接表

        在图的表示方法中,邻接矩阵和邻接表是两种常见的方式。邻接矩阵使用二维数组存储图中的连接关系,而邻接表使用链表存储每个节点的相邻节点。本文将设计一个算法,将无向图的邻接矩阵转换为对应的邻接表。

1. 理解问题

        给定一个无向图的邻接矩阵,设计一个算法将其转换为邻接表。无向图的邻接矩阵是一个对称的二维数组,矩阵中的值表示两个节点之间是否有连接。我们需要基于邻接矩阵中的信息构建邻接表,其中每个节点都有一个链表存储其所有的相邻节点。

示例:

假设我们有如下的无向图邻接矩阵(4 个节点):

    0  1  2  3
0 [ 0, 1, 1, 0 ]
1 [ 1, 0, 0, 1 ]
2 [ 1, 0, 0, 1 ]
3 [ 0, 1, 1, 0 ]
转换后的邻接表表示如下:

0: 1 -> 2
1: 0 -> 3
2: 0 -> 3
3: 1 -> 2

关键点:

  • 邻接矩阵:使用二维数组存储图中节点之间的连接关系。矩阵中值为 1 表示节点之间有边,值为 0 表示没有边。

  • 邻接表每个节点对应一个链表,链表中的元素表示与该节点直接相连的节点。

2. 输入输出

输入:

  • matrix:一个二维数组,表示无向图的邻接矩阵。

输出:

  • 一个邻接表,使用链表表示每个节点的相邻节点。

3. 数据结构

        邻接矩阵使用一个二维数组表示:

int matrix[MAX][MAX];
        邻接表中的每个节点定义为一个结构体链表节点:

struct ListNode {
    int vertex;               // 相邻的节点
    struct ListNode* next;    // 指向下一个相邻节点
};
        使用一个数组来存储邻接表,其中每个元素指向一个链表头节点,表示与该节点直接相连的所有节点。

struct ListNode* adjList[MAX];

4. 制定策略

为了将邻接矩阵转换为邻接表,我们可以按照以下步骤进行:

  1. 遍历矩阵:对于邻接矩阵中的每一个元素 matrix[i][j],如果值为 1,则表示节点 i 和节点 j 之间有一条边。我们需要将节点 j 添加到节点 i 的邻接表中,并且由于无向图的对称性,也要将节点 i 添加到节点 j 的邻接表中

  2. 创建邻接表:对于每个节点 i,我们动态分配链表节点,将每个相邻的节点(即 matrix[i][j] == 1 时)添加到链表中。

5. 实现代码

5.1 关键函数实现

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

// 定义邻接表节点
struct ListNode {
    int vertex;               // 相邻节点
    struct ListNode* next;    // 指向下一个相邻节点
};

// 创建一个新节点
struct ListNode* createNode(int v) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    newNode->vertex = v;
    newNode->next = NULL;
    return newNode;
}

// 将邻接矩阵转换为邻接表
void convertMatrixToAdjList(int matrix[][MAX], int n, struct ListNode* adjList[]) {
    for (int i = 0; i < n; i++) {
        adjList[i] = NULL;  // 初始化每个节点的邻接表为空

        for (int j = 0; j < n; j++) {
            if (matrix[i][j] == 1) {  // 如果存在边
                struct ListNode* newNode = createNode(j);  // 创建一个新节点
                newNode->next = adjList[i];  // 将新节点插入链表头部
                adjList[i] = newNode;        // 更新链表头节点
            }
        }
    }
}

5.2 模拟过程

        假设输入的邻接矩阵如下:

    0  1  2  3
0 [ 0, 1, 1, 0 ]
1 [ 1, 0, 0, 1 ]
2 [ 1, 0, 0, 1 ]
3 [ 0, 1, 1, 0 ]

Step 1: 初始化邻接表

对于每个节点 i 初始化链表为 NULL。

Step 2: 遍历矩阵,生成邻接表

  • 处理节点 0

    • 邻接矩阵 matrix[0][1] = 1,将节点 1 插入到节点 0 的邻接表中。

    • 邻接矩阵 matrix[0][2] = 1,将节点 2 插入到节点 0 的邻接表中。

    最终,节点 0 的邻接表为:0: 2 -> 1

  • 处理节点 1

    • 邻接矩阵 matrix[1][0] = 1,将节点 0 插入到节点 1 的邻接表中。

    • 邻接矩阵 matrix[1][3] = 1,将节点 3 插入到节点 1 的邻接表中。

    最终,节点 1 的邻接表为:1: 3 -> 0

  • 处理节点 2

    • 邻接矩阵 matrix[2][0] = 1,将节点 0 插入到节点 2 的邻接表中。

    • 邻接矩阵 matrix[2][3] = 1,将节点 3 插入到节点 2 的邻接表中。

    最终,节点 2 的邻接表为:2: 3 -> 0

  • 处理节点 3

    • 邻接矩阵 matrix[3][1] = 1,将节点 1 插入到节点 3 的邻接表中。

    • 邻接矩阵 matrix[3][2] = 1,将节点 2 插入到节点 3 的邻接表中。

    最终,节点 3 的邻接表为:3: 2 -> 1

5.3 完整c代码

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

// 定义邻接表节点结构
struct ListNode {
    int vertex;               // 相邻节点的编号
    struct ListNode* next;    // 指向下一个相邻节点
};

// 创建一个新节点,表示某个相邻节点
struct ListNode* createNode(int v) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));  // 分配内存给新节点
    newNode->vertex = v;      // 设置相邻节点编号
    newNode->next = NULL;     // 初始化 next 指针为 NULL
    return newNode;           // 返回新节点
}

// 将邻接矩阵转换为邻接表
void convertMatrixToAdjList(int matrix[][100], int n, struct ListNode* adjList[]) {
    // 遍历邻接矩阵
    for (int i = 0; i < n; i++) {
        adjList[i] = NULL;  // 初始化每个节点的邻接表为空

        for (int j = 0; j < n; j++) {
            if (matrix[i][j] == 1) {  // 如果存在边,matrix[i][j] = 1
                struct ListNode* newNode = createNode(j);  // 创建一个新节点
                newNode->next = adjList[i];  // 新节点的 next 指向当前邻接表的头节点
                adjList[i] = newNode;        // 更新邻接表的头节点为新节点
            }
        }
    }
}

// 打印邻接表
void printAdjList(struct ListNode* adjList[], int n) {
    for (int i = 0; i < n; i++) {
        struct ListNode* temp = adjList[i];  // 获取当前节点的邻接表
        printf("%d: ", i);                   // 打印节点编号

        // 遍历并打印链表中的所有相邻节点
        while (temp) {
            printf("%d -> ", temp->vertex);  // 打印相邻节点
            temp = temp->next;               // 移动到下一个相邻节点
        }
        printf("NULL\n");                    // 链表结束
    }
}

int main() {
    int n;  // 图中节点的数量
    printf("请输入图中节点的数量: ");
    scanf("%d", &n);

    // 初始化邻接矩阵
    int matrix[100][100];
    printf("请输入邻接矩阵:\n");
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &matrix[i][j]);  // 输入邻接矩阵
        }
    }

    // 初始化邻接表
    struct ListNode* adjList[100];

    // 将邻接矩阵转换为邻接表
    convertMatrixToAdjList(matrix, n, adjList);

    // 打印邻接表
    printf("\n邻接表表示:\n");
    printAdjList(adjList, n);

    return 0;
}


5.4 代码说明

        convertMatrixToAdjList(int matrix[][100], int n, struct ListNode* adjList[]):将邻接矩阵转换为邻接表

  • 参数:

    • matrix:存储图的邻接矩阵,matrix[i][j] 表示节点 i 和节点 j 是否有边。

    • n:节点的数量。

    • adjList:邻接表数组,存储每个节点对应的链表。

  • 通过遍历邻接矩阵,如果发现 matrix[i][j] == 1,表示节点 i 和节点 j 之间有边,此时将节点 j 添加到节点 i 的邻接表中。

  • 由于是无向图,因此 matrix[i][j] == matrix[j][i],我们只需处理一遍即可。

6. 运行结果

        转换后的邻接表表示如下:

0: 2 -> 1
1: 3 -> 0
2: 3 -> 0
3: 2 -> 1

7. 时间和空间复杂度分析

        时间复杂度:O(n²)

        遍历邻接矩阵时需要检查每一个元素,因此时间复杂度为 O(n²),其中 n 为图中节点的数量。

        空间复杂度:O(n + e)

        邻接表中每个节点最多有 n 个链表头节点,而边的数量为 e,因此空间复杂度为 O(n + e)。

8. 总结

        通过该算法,我们能够将无向图的邻接矩阵转换为邻接表。相较于邻接矩阵,邻接表在表示稀疏图时更加节省空间,适合边数较少的图。该算法的核心是矩阵遍历和链表操作,通过简单的指针操作实现图的结构转换。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com