C数据结构——无向图(邻接表方式) 创建与基本使用

news2024/11/17 3:56:30

源码+注释

//
// Created by Lenovo on 2022-05-17-下午 4:37.
// 作者:小象
// 版本:1.0
//

#include <stdio.h>
#include <malloc.h>

#define TRUE 1
#define FALSE 0

#define MAX_ALVNUMS 100 // 最大顶点数

/*
 * 定义链队
 */
typedef int QElemType;

typedef struct QNode {
    QElemType data;
    struct QNode *next;
} QNode, *QueuePtr;

typedef struct {
    QueuePtr front; // 队头指针
    QueuePtr rear; // 队尾指针
} LinkQueue;

/*
 * 定义邻接表
 */
// 边结点
typedef struct EdgeNode { // 结构体名称(用于结构体自身调用)
    int adjvex; // 该边指向顶点的位置
    struct EdgeNode *next; // 指向下一条边的指针
} EdgeNode;

// 顶点信息
typedef struct VertexNode {
    char verxs;
    EdgeNode *firstEdge; // 指向第一条依附该边的边的指针
} AdjList[MAX_ALVNUMS]; // AdjList 表示邻接表类型

// 邻接表
typedef struct {
    AdjList adjList;
    int numVertexes, numEdges; // 图的当前顶点数和边数
} ALGraph;

int visited[MAX_ALVNUMS]; // 访问标志数组,其初值为"false"

void CreateALGraph(ALGraph *G); // 用邻接表表示法创建无向图
void PrintAMatrix(ALGraph G); // 打印邻接表
void DFSTraverse(ALGraph G, int v); // 非连通图 深度优先搜索遍历
void DFS_AL(ALGraph G, int v); // 邻接矩阵 深度优先搜索遍历(连通图)
void BFSTraverse(ALGraph G, int v); // 非连通图 广度优先搜索遍历
void BFS_AL(ALGraph G, int v); // 邻接矩阵 广度优先搜索遍历(连通图)
int InitQueue(LinkQueue *queue); // 链队的初始化
int EnQueue(LinkQueue *queue, QElemType elem); // 链队入队列
int DeQueue(LinkQueue *queue, QElemType *elem); // 链队出队列
int IsEmpty(LinkQueue *queue); // 判队空

/**
 * <h2>无向图的创建和遍历(无向网同理)</h2>
 * <h3>无向图邻接表特点: <br>
 * 两个顶点确定一条边的位置,此边两点就有必定相邻 <br>
 * @return 0
 */
int main() {

    ALGraph *G;
    G = (ALGraph *) malloc(sizeof(ALGraph)); // 为图G生成内存空间

    CreateALGraph(G); // 创建
    PrintAMatrix(*G); // 输出邻接表
    DFSTraverse(*G, 0); // 深度搜索
    printf("\n");
    BFSTraverse(*G, 1); // 深度搜索

    getchar();
}

// 使用邻接表表示法,创建无向图
void CreateALGraph(ALGraph *G) {

    // 输入总顶点数和边数
    printf("输入无向图的顶点数和边数,用空格分开:");
    scanf("%d %d", &(G->numVertexes), &(G->numEdges));
    getchar();

    // 依次输入顶点的信息,构造表头结点表
    for (int i = 0; i < G->numVertexes; i++) {
/*        printf("输入第%d个顶点信息:", (i + 1));
        scanf("%c", &G->verxs[i]);
        getchar();*/
        G->adjList[i].verxs = i; // 输入顶点值
        G->adjList[i].firstEdge = NULL; // 初始化表头结点的指针域为空
    }

    // 输入各边,构造邻接表
    int arrSub1[] = {0, 0, 1, 1, 2, 2, 3, 4, 5};
    int arrSub2[] = {1, 2, 3, 4, 5, 6, 7, 7, 6};
    for (int i = 0; i < G->numEdges; i++) {
/*        printf("输入第%d条边(空格分开):", (i + 1)); // 输入一条边依附的两个顶点
        scanf("%d %d", &sub1, &sub2); // 确定v1和v2在G中的位置,即顶点在G.verxs中的序号
        getchar();*/

        /*
         * 使用链表中头插法的方式建立邻接表
         */
        // 生成一个新的边结点 *edgeNode
        EdgeNode *edgeNode = (EdgeNode *) malloc(sizeof(EdgeNode));
        // 临接点序号为 sub2
        edgeNode->adjvex = arrSub2[i];
        // 将新结点*edgeNode 插入顶点 v[sub1]的边表头部
        edgeNode->next = G->adjList[arrSub1[i]].firstEdge;
        G->adjList[arrSub1[i]].firstEdge = edgeNode;

        // 生成另一个对称的新的边结点 edgeNode
        edgeNode = (EdgeNode *) malloc(sizeof(EdgeNode));
        // 邻接点序号为 sub1
        edgeNode->adjvex = arrSub1[i];
        // 将新结点 edgeNode 插入顶点 v[sub2]的边表头部
        edgeNode->next = G->adjList[arrSub2[i]].firstEdge;
        G->adjList[arrSub2[i]].firstEdge = edgeNode;
    }
}

// 打印邻接表
void PrintAMatrix(ALGraph G) {
    printf("============= 无向图G的邻接表 =============\n");
    /*
     * 上
     */
    for (int i = 0; i < G.numVertexes; i++) {
        if (i == 0) {
            printf("   .--------.");
            for (EdgeNode *edgeNode = G.adjList[i].firstEdge;
                 edgeNode != NULL; edgeNode = edgeNode->next) {
                printf("   .-------.");
            }
            printf("\n");
        }
        /*
         * 中
         */
        printf(" %d ", i);
        // 打印头结点信息
        printf("| V%d | --|-->", G.adjList[i].verxs);
        // 打印结点信息
        for (EdgeNode *edgeNode = G.adjList[i].firstEdge;
             edgeNode != NULL; edgeNode = edgeNode->next) {
            if (edgeNode->next != NULL) {
                printf("| %d | --|", edgeNode->adjvex);
                printf("-->");
            } else {
                printf("| %d | ^ |", edgeNode->adjvex);
            }
        }
        printf("\n");
        /*
         * 下
         */
        if (i != G.numVertexes - 1) {
            printf("   |----+---|");
            for (EdgeNode *edgeNode = G.adjList[i].firstEdge;
                 edgeNode != NULL; edgeNode = edgeNode->next) {
                printf("   :=======:");
            }
        } else {
            printf("   '--------'");
            for (EdgeNode *edgeNode = G.adjList[i].firstEdge;
                 edgeNode != NULL; edgeNode = edgeNode->next) {
                printf("   '-------'");
            }
        }
        printf("\n");
    }
}

// 对非连通图G做 深度优先遍历
void DFSTraverse(ALGraph G, int v) {
    // 注意:对于以下所有循环次数解析,最多有numVertexes顶点个连通图,所以循环numVertexes次就足够保证整个非连通图都被遍历到
    for (int i = 0; i < G.numVertexes; i++) {
        visited[i] = FALSE; // 访问标志数组初始化
    }
    for (int i = 0; i < G.numVertexes; i++) { // 循环调用遍历连通图的算法函数
        if (!visited[i]) {
            DFS_AL(G, v); // 对尚未访问的顶点调用DFS_AL()
        }
    }
}

// 图G为邻接表类型,从第v个顶点出发 深度优先搜索遍历 图G
void DFS_AL(ALGraph G, int v) {
    printf("V%d ", v);
    visited[v] = TRUE; // 访问第v个顶点,并置访问标志数组相应分量值为true
    EdgeNode *edgeNode = G.adjList[v].firstEdge; // edgeNode 指向 v 的边链表的第一个边结点
    while (edgeNode != NULL) { // 边结点非空

        int i = edgeNode->adjvex; // 表示 i 是 v的邻接点
        if (!visited[i]) { // 如果 i 未访问,则递归调用DFS_AL
            DFS_AL(G, i);
        }

        edgeNode = edgeNode->next; // edgeNode 指向下一个边结点
    }
}

// 对非连通图G做 广度优先遍历
void BFSTraverse(ALGraph G, int v) {
    // 注意:对于以下所有循环次数解析,最多有numVertexes顶点个连通图,所以循环numVertexes次就足够保证整个非连通图都被遍历到
    for (int i = 0; i < G.numVertexes; i++) {
        visited[i] = FALSE; // 访问标志数组初始化
    }
    for (int i = 0; i < G.numVertexes; i++) { // 循环调用遍历连通图的算法函数
        if (!visited[i]) {
            BFS_AL(G, v); // 对尚未访问的顶点调用BFS_AL()
        }
    }
}

// 图G为邻接表类型,从第v个顶点出发 广度优先搜索遍历 图G
void BFS_AL(ALGraph G, int v) {
    printf("V%d ", v);
    visited[v] = TRUE; // 访问第v个顶点,并置访问标志数组相应分量值为 true
    LinkQueue queue;
    InitQueue(&queue); // 辅助队列queue初始化,置空
    EnQueue(&queue, v); // v 进队
    int u;
    while (!IsEmpty(&queue)) {
        DeQueue(&queue, &u); // 队头元素出队并置为 u
        EdgeNode *edgeNode = G.adjList[u].firstEdge;
        while (edgeNode != NULL) {
            if ((!visited[edgeNode->adjvex])) {
                printf("V%d ", edgeNode->adjvex);
                visited[edgeNode->adjvex] = TRUE; // 访问第 edgeNode->adjvex 个顶点,并置访问标志数组相应分量值为true
                EnQueue(&queue, edgeNode->adjvex);  // edgeNode->adjvex 进队
            }
            edgeNode = edgeNode->next;
        }
    }
}

// 构造一个空队列
int InitQueue(LinkQueue *queue) {
    queue->front = queue->rear = (QueuePtr) malloc(sizeof(QNode)); // 生成新结点作为头结点,队头和队尾指针指向此结点
    if (!(queue->front)) {
        return FALSE;
    }
    queue->front->next = NULL; // 头结点指针域置空
    return TRUE;
}

// 插入元素elem为queue的新的队尾元素
int EnQueue(LinkQueue *queue, QElemType elem) {
    QueuePtr temp;
    temp = (QueuePtr) malloc(sizeof(QNode)); // 为入队元素分配结点空间,用指针temp指向
    if (!temp) {
        return FALSE;
    }
    temp->data = elem; // 将新结点数据域置为elem
    temp->next = NULL;
    queue->rear->next = temp; // 将新结点插入到队尾
    queue->rear = temp; // 修改队尾指针
    return TRUE;
}

// 删除queue的队头元素,用elem返回其值
int DeQueue(LinkQueue *queue, QElemType *elem) {
    if (IsEmpty(queue)) {
        return FALSE; // 若队列空,则返回FALSE
    }
    QueuePtr pQ;
    pQ = queue->front->next; // pQ指向队头元素
    *elem = pQ->data; // *elem保存队头元素的值
    queue->front->next = pQ->next; // 修改头结点的指针域
    if (queue->rear == pQ) {
        queue->rear = queue->front; // 最后一个元素被删,队尾指针指向头结点
    }
    free(pQ); // 释放原队头元素空间
    return TRUE;
}

// 判队空
int IsEmpty(LinkQueue *queue) {
    return queue->front == queue->rear;
}

运行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/810183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务器部署Go项目

最近在研究服务器部署项目&#xff0c;用了好几种办法成功部署。这些方法互有利弊&#xff0c;本文就逐一详细演示说明&#xff1a; 目录 1.服务器下载Go环境&#xff0c;直接将项目代码放到服务器上运行 2.服务器不下载Go环境&#xff0c;本地将项目打包成可执行的二进制…

【小程序】快来开发你的第一个微信小游戏(详细流程)

&#x1f973; 作者&#xff1a;伯子南 &#x1f60e; 坚信&#xff1a; 好记性不如乱笔头&#xff0c;独乐乐不如众乐乐 &#x1f4aa; 个人主页&#xff1a;https://blog.csdn.net/qq_34577234?spm1010.2135.3001.5421 &#x1f46c;&#x1f3fb; 觉得博主文章不错的话&…

java基础String类的特性

文章目录 1 String的特性2 String的内存结构1&#xff1a;拼接相关2&#xff1a;new相关3&#xff1a;intern() 1 String的特性 java.lang.String 类代表字符串。Java程序中所有的字符串文字&#xff08;例如"hello" &#xff09;都可以看作是实现此类的实例。 字符…

Python面向对象(二)(构造方法、其它内置方法)

构造方法 属性&#xff08;成员变量&#xff09;的赋值 class Student:name None # 姓名age None # 年龄tel None # 手机号stu1 Student() stu1.name "张三" stu1.age 20 stu1.tel "1212121211"stu2 Student() stu2.name "张…

Pytorch气温预测实战

数据集 数据有8个特征&#xff0c;一个标签值 自变量因变量yearactual:当天的真实最高温度monthdayweek:星期几temp_1:昨天的最高温度temp_2:前天的最高温度值average:在历史中&#xff0c;每年这一天的平均最高温度friend:朋友猜测的温度 year,month,day,week,temp_2,temp_…

【Linux】HTTPS协议是如何保证数据安全的

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录 &#x1f449;基础概念…

【Linux】进程篇Ⅰ:进程信息、进程状态、环境变量、进程地址空间

文章目录 一、概述二、查看进程信息1. 系统文件夹 /proc2. 用户级工具 ps3. getpid() 函数&#xff1a;查看进程 PID4. 用 kill 杀进程5. 进程优先级 二、进程状态分析0. 1. R (running) 运行状态2. S (sleeping) 休眠状态3. D (disk sleep) 不可中断的休眠状态4. T (stopped) …

【数据结构】顺序表(SeqList)(增、删、查、改)详解

一、顺序表的概念和结构 1、顺序表的概念&#xff1a; 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改。 2、顺序表的结构&#xff1a; &#xff08;1&#xff09;静态顺序表&#xff1a;使…

Redis Cluster 在Spring中遇到的问题

Redis集群配置可能会在运行时更改。可以添加新节点&#xff0c;可以更改特定插槽的主节点。还有可能因为master宕机或网络抖动等原因&#xff0c;引起了主从切换。 无法感知集群槽位变化 SpringBoot2.x 开始默认使用的 Redis 客户端由 Jedis 变成了 Lettuce&#xff0c;但是当…

忽略nan值,沿指定轴计算标准(偏)差numpy.nanstd()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 沿指定轴方向 计算标准(偏)差 numpy.nanstd() [太阳]选择题 import numpy as np a np.array([[1,2],[np.nan,3]]) print("【显示】a ") print(a) print("【执行】np.std(a)&qu…

QT项目代码去UI界面常用开发步骤

QT项目代码去UI界面常用开发步骤 因项目开发需求&#xff0c;领导要求整个QT项目中不要用UI方式来实现界面&#xff0c;这样能保障程序运行稳定性以及代码的逻辑和可读性,先记录具体操作步骤如下&#xff1a; 1、首先我们通过拖控件的方式来实现界面的设计效果&#xff0c…

ARM汇编基本变量的定义和使用

一、ARM汇编中基本变量是什么? 数字变量: GBLA LCLA SETA 逻辑变量:GBLL LCLL SETL 字符串:GBLS LCLS SETLS 注意需要TAB键定义变量和行首改变值 二、使用步骤 1.引入库 代码如下(示例): GBLA led_num Reset_Handler PROCEXPORT Reset_Handler [WEA…

HCIP BGP综合实验

题目 1、AS1存在两个环回&#xff0c;一个地址为192.168.1.0/24该地址不能在任何协议中宣告&#xff1b; 2、AS3中存在两个环回&#xff0c;一个地址为192.168.2.0/24该地址不能在任何协议中宣告&#xff0c;最终要求这两个环回可以互相通讯&#xff1b; 3、AS间的骨干链路I…

Vue3搭建启动

Vue3搭建&启动 一、创建项目二、启动项目三、配置项目1、添加编辑器配置文件2、配置别名3、处理sass/scss4、处理tsx 四、添加Eslint 一、创建项目 npm create vite 1.project-name 输入项目名vue3-vite 2.select a framework 选择框架 3.select a variant 选择语言 二、启…

idea 安装 插件jrebel 报错LS client not configured.

这个报错找了好久&#xff0c;有博主说版本不对&#xff0c;我脑子没反应过来以为是随便换一个低版本的就行&#xff0c;没想到只能是2022.4.1 这个版本才行 一定要用jrebel 2022.4.1的插件版本&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 插件下载地址&…

网络面试合集

传输层的数据结构是什么&#xff1f; 就是在问他的协议格式&#xff1a;UDP&TCP 2.1.1三次握手 通信前&#xff0c;要先建立连接&#xff0c;确保双方都是在线&#xff0c;具有数据收发的能力。 2.1.2四次挥手 通信结束后&#xff0c;会有一个断开连接的过程&#xff0…

❤️创意网页:绚丽粒子雨动画

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

Codeforces Round 889 (Div. 2) 题解

晚上睡不着就来总结一下叭~&#xff08;OoO&#xff09; 赛后榜(希望不要被Hack...Orz) 终榜&#xff01;&#xff01;&#xff01; 瞬间的辉煌(呜呜呜~) 先不放图了。。怕被dalaoHack...呜呜呜~ 总结 7.29半夜比赛&#xff0c;本来是不想打的&#xff0c;感觉最近做的题太多…

Manjaro KDE 22.1.3vmware无法复制文件

Wayland 是 X11 的现代替代品&#xff0c;几十年来 X11 一直是 Linux 上的默认窗口系统。 Wayland 是一种通信协议&#xff0c;定义 X Window 显示服务器和客户端应用程序之间的消息传递。 软件还不兼容 使用X11即可

JavaScript中的switch语句

switch语句和if语句一样&#xff0c;同样是运用于条件循环中&#xff1b; 下面例子我们用switch实现 例如如果今天是周一就学习HTML&#xff0c;周二学习CSS和JavaScript&#xff0c;周三学习vue&#xff0c;周四&#xff0c;周五学习node.js&#xff0c;周六周日快乐玩耍&…