【C 数据结构-图】2. 图的存储结构

news2024/11/27 9:50:34

文章目录

  • 【 1. 图的顺序存储结构 】
    • 1.1 基本原理
    • 1.2 顺序存储结构的 C 实现
  • 【 2. 图的链式存储结构 】
    • 2.1 图的临接表存储结构
      • 2.1.1 临接表的 基本原理
      • 2.1.2 临接表的 链表节点
      • 2.1.3 邻接表 各结构体的C实现
      • 2.1.4 临接表 计算顶点的出度和入度
        • 邻接表计算 无向图的出度和入度
        • 邻接表计算 有向图的出度和入度
    • 2.2 图的十字链表存储结构
      • 2.2.1 十字链表的 基本原理
      • 2.2.2 十字链表的 链表节点
      • 2.2.3 十字链表的 C实现
    • 2.3 图的临接多重表存储结构
      • 2.3.1 临接多重表的 基本原理
      • 2.3.2 临接多重表的 链表节点
      • 2.3.3 临接多重表的 C 实现
  • 【 3. 图的各存储结构的对比 】

【 1. 图的顺序存储结构 】

1.1 基本原理

  • 图可以采用顺序存储(也就是使用 数组 有效地存储),使用数组存储图时,需要使用 两个数组
    • 数据数组:存放图中顶点本身的数据(一维数组:存储图中各顶点本身数据,使用一维数组就足够了);
    • 临接矩阵(关系数组):用于存储各顶点之间的关系(二维数组:存储顶点之间的关系时,要记录每个顶点和其它所有顶点之间的关系,所以需要使用二维数组)。临接矩阵中值的确定如下:
      • 在使用二维数组存储 无权值 的图 中顶点之间的关系时,如果顶点之间 存在边或弧,在相应位置用 1 表示,反之用 0 表示
      • 如果使用二维数组存储 有权值 的图即网 中顶点之间的关系,顶点之间如果 有边或者弧的存在,在数组的相应位置存储其权值;反之用 0 表示
  • 数组 可用于存储 无向图有向图

例如:存储下图中的两张图时,除了存储图中各顶点本身具有的数据外,还需要使用二维数组存储任意两个顶点之间的关系。
在这里插入图片描述

  • 存储上图中的有向图(A)时,对应的二维数组如下图所示:
    例如,arcs[0][1] = 1 ,证明从 V1 到 V2 有弧存在。且通过该矩阵,可以很轻松得知各顶点的出度和入度,出度为该行非 0 值的和,入度为该列非 0 值的和。例如,V1 的出度为第一行两个 1 的和 为 2 ; V1 的入度为第一列中 1 的和 为 1 。所以 V1 的出度为 2 ,入度为 1 ,度为两者的和 3 。
    在这里插入图片描述
  • 存储上上图中的无向图(B)时,由于各顶点没有权值,所以如果两顶点之间有关联,相应位置记为 1 ;反之记为 0 。构建的二维数组如下图所示:
    在此二维数组中,每一行代表一个顶点,依次从 V1 到 V5 ,每一列也是如此。比如 arcs[0][1] = 1 ,表示 V1 和 V2 之间有边存在;而 arcs[0][2] = 0,说明 V1 和 V3 之间没有边。通过该矩阵,可以直观地判断出各个顶点的度,为该行(或该列)非 0 值的和。例如,第一行有两个 1,说明 V1 有两个边,所以度为 2。
    在这里插入图片描述

1.2 顺序存储结构的 C 实现

  • 在此程序中,构建无向网或有向网时,对于之间没有边或弧的顶点,相应的二阶矩阵中存放的是 0,目的只是为了方便查看运行结果。而实际上 如果顶点之间没有关联,则关系数组对应位置上的值应该是无穷大 ∞,以表示距离无穷远,根本无法到达
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define MAX_VERtEX_NUM 20  //顶点的最大个数
#define VertexType int     //顶点的数据类型
#define VRType int         //表示顶点之间的关系的变量类型
#define InfoType char      //存储弧或者边额外信息的指针变量类型
typedef enum { DG, DN, UDG, UDN }GraphKind;  //枚举图的 4 种类型:0无权有向、1无权无向、2有权有向、3有权无向

// 边/弧 的信息结构体,包括权值和附加其他信息
typedef struct {
    VRType adj;     //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。
    InfoType* info; //弧或边额外含有的信息指针
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];

//图结构体
typedef struct {
    VertexType vexs[MAX_VERtEX_NUM];  //一维数据数组
    AdjMatrix arcs;                   //二维关系数组(邻接矩阵),每个元素为1个ArcCell信息结构体
    int vexnum, arcnum;               //图的顶点数和 弧/边 数
    GraphKind kind;                   //图的种类
}MGraph;

//根据顶点本身数据值,判断出顶点在二维数组中的位置
int LocateVex(MGraph* G, VertexType v) {
    int i = 0;
    //遍历一维数组,找到变量v
    for (; i < G->vexnum; i++) {
        if (G->vexs[i] == v) {
            break;
        }
    }
    //如果找不到,输出提示语句,返回-1
    if (i > G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}
//构造无权有向图
void CreateDG(MGraph* G) {
    //输入图含有的顶点数和弧的个数
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    //依次输入顶点本身的数据
    for (int i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    //初始化二维矩阵,全部归0,指针指向NULL
    for (int i = 0; i < G->vexnum; i++) {
        for (int j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }
    //在二维数组中添加弧的数据
    for (int i = 0; i < G->arcnum; i++) {
        int v1, v2;
        //输入弧头和弧尾
        scanf("%d %d", &v1,&v2);
        //确定顶点位置
        int n = LocateVex(G, v1);
        int m = LocateVex(G, v2);
        //排除错误数据
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        //将正确的弧的数据加入二维数组
        G->arcs[n][m].adj = 1;
    }
}
//构造无权无向图
void CreateDN(MGraph* G) {
    scanf("%d %d", &(G->vexnum), &(G->arcnum));
    for (int i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    for (int i = 0; i < G->vexnum; i++) {
        for (int j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }
    for (int i = 0; i < G->arcnum; i++) {
        int v1, v2;
        scanf("%d,%d", &v1, &v2);
        int n = LocateVex(G, v1);
        int m = LocateVex(G, v2);
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj = 1;
        G->arcs[m][n].adj = 1;//无向图的二阶矩阵沿主对角线对称
    }
}
//构造有权有向网
void CreateUDG(MGraph* G) {
    scanf("%d %d", &(G->vexnum), &(G->arcnum));
    for (int i = 0; i < G->vexnum; i++) {
        scanf("%d",&(G->vexs[i]));
    }
    for (int i = 0; i < G->vexnum; i++) {
        for (int j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }
    for (int i = 0; i < G->arcnum; i++) {
        int v1, v2, w;
        scanf("%d %d %d", &v1, &v2, &w);
        int n = LocateVex(G, v1);
        int m = LocateVex(G, v2);
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj = w;
    }
}
//构造有权无向网
void CreateUDN(MGraph* G) {
    scanf("%d %d", &(G->vexnum), &(G->arcnum));
    for (int i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    for (int i = 0; i < G->vexnum; i++) {
        for (int j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }
    for (int i = 0; i < G->arcnum; i++) {
        int v1, v2, w;
        scanf("%d %d %d", &v1, &v2, &w);
        int m = LocateVex(G, v1);
        int n = LocateVex(G, v2);
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj = w;
        G->arcs[m][n].adj = w;//矩阵对称
    }
}
void CreateGraph(MGraph* G) {
    //选择图的类型
    scanf("%d", &(G->kind));
    //根据所选类型,调用不同的函数实现构造图的功能
    switch (G->kind) 
    {
	case DG:
		return CreateDG(G);
		break;
	case DN:
		return CreateDN(G);
		break;
	case UDG:
		return CreateUDG(G);
		break;
	case UDN:
		return CreateUDN(G);
		break;
	default:
		break;
	}
}
//输出函数
void PrintGrapth(MGraph G)
{
    for (int i = 0; i < G.vexnum; i++)
    {
        for (int j = 0; j < G.vexnum; j++)
        {
            printf("%d ", G.arcs[i][j].adj);
        }
        printf("\n");
    }
}

int main()
{
    MGraph G;//建立一个图的变量,对象
    CreateGraph(&G);//调用创建函数,传入地址参数,进行初始化
    PrintGrapth(G); //输出图的二阶矩阵
    return 0;
}
  • 例如,使用上述程序存储下图 (a)的有向网时,存储的两个数组如下图 (b)所示:
    在这里插入图片描述
//输入下列数据:
2
6 10
1
2
3
4
5
6
1 2 5
2 3 4
3 1 8
1 4 7
4 3 5
3 6 9
6 1 3
4 6 6
6 5 1
5 4 5

在这里插入图片描述

【 2. 图的链式存储结构 】

  • 通常,图更多的是采用链表存储,具体的存储方法有 3 种,分别是邻接表、邻接多重表和十字链表。

2.1 图的临接表存储结构

2.1.1 临接表的 基本原理

  • 如果图中的两个点相互连通,即通过其中一个顶点,可直接找到另一个顶点,则称它们互为 邻接点 邻接 指的是图中顶点之间有边或者弧的存在。邻接表 可用于存储 无向图有向图
  • 邻接表的实现方式:
    • 给图中的各个顶点独自建立一个链表,用 首元节点存储该顶点,用链表中 其他节点存储各自的临接点
    • 同时,为了便于管理这些链表,通常会将所有 链表的头节点存储到数组中(也可以用链表存储), 各链表在存储顶点的临接点时,仅需存储该邻接点位于数组中的下标 即可。
  • 例如,存储下图 a 所示的有向图,其对应的邻接表如下图 b 所示:
    以顶点 V1 为例,与其相关的邻接点分别为 V2 和 V3,因此存储 V1 的链表中存储的是 V2 和 V3 在数组中的位置下标 1 和 2。
    在这里插入图片描述
  • 对于具有 n 个顶点和 e 条边的无向图,邻接表中需要存储 n 个头结点和 2e 个链表中的结点。在图中边或者弧稀疏的时候,使用邻接表(所有链表中的节点总数为 n + 2 e n+2e n+2e)要比图的顺序存储结构中的邻接矩阵(数组大小为 n 2 n^2 n2) 更加节省空间。

2.1.2 临接表的 链表节点

  • 存储各顶点的节点结构分为两部分,数据域和指针域。data 数据域用于存储顶点数据信息,next 指针域用于指向下一个临接点,如下图所示:
    在这里插入图片描述
  • 在实际应用中,除了上图这种节点结构外,对于用临接表存储网(边或弧存在权)结构,还需要节点存储权的值,因此需使用下图中的节点结构:adjvex为邻接点在数组中的下标,next指针指向下一个节点,info 表示 adjvex 所代表的顶点与临接表表头所代表顶点的权值。
    在这里插入图片描述

2.1.3 邻接表 各结构体的C实现

  • 邻接表 各结构体的 C 实现:
#define  MAX_VERTEX_NUM 20	//顶点的最大个数
#define  VertexType int		//顶点的数据类型
#define  InfoType int		//弧或者边包含的信息的类型

//各顶点的临接点结构体
typedef struct ArcNode{
    int adjvex;//邻接点在数组中的下标
    struct ArcNode * nextarc;//指向下一个邻接点
    InfoType * info;//信息域
}ArcNode;

//图的顶点结构体
typedef struct VNode{
    VertexType data;   //顶点的数据域
    ArcNode * firstarc;//指向邻接点的指针
}VNode,AdjList[MAX_VERTEX_NUM];//存储各链表头结点的数组

//图结构体
typedef struct {
    AdjList vertices;  //图中顶点的数组
	int vexnum,arcnum; //图中顶点数和边/弧数
    int kind; //图的种类
}ALGraph;

2.1.4 临接表 计算顶点的出度和入度

邻接表计算 无向图的出度和入度
  • 使用邻接表 计算 无向图 中顶点的入度和出度只需从数组中 找到该顶点然后统计此链表中节点的数量 即可
邻接表计算 有向图的出度和入度
  • 使用 邻接表存储 有向图 时,通常各个顶点的链表中存储的都是以该顶点为弧尾的邻接点,因此 通过统计各顶点链表中的节点数量, 只能计算出该顶点的出度,而无法计算该顶点的入度
  • 对于利用邻接表求某顶点的入度,有两种方式:
    ① 遍历整个邻接表中的节点,统计除了该顶点外所有的链表中与该顶点所在数组位置下标相同的节点数量,即为该顶点的入度;
    ② 建立一个 逆邻接表,该表中的各顶点链表专门用于存储以此顶点为弧头的所有顶点在数组中的位置下标。
    比如说,建立一张2.1.1中图 a 对应的逆邻接表,如下图所示:
    以 V1 为例,其他顶点中能到达 V1 的顶点为V4,故 顶点V1的链表中第二个节点存储的是 V4 所在数组的下标3。
    在这里插入图片描述

2.2 图的十字链表存储结构

2.2.1 十字链表的 基本原理

  • 十字链表法 仅适用于 存储有向的,即 有向图有向网。不仅如此,十字链表法还 解决了邻接表不方便计算有向图顶点入度的问题
  • 十字链表存储有向图(网)的方式与邻接表有一些相同,都 以图(网)中各顶点为首元节点 建立多条链表,同时为了便于管理,还将所有链表的首元节点存储到同一数组(或链表)中。
  • 十字链表实质上就是为每个顶点建立两个链表,分别存储以该顶点为弧头的所有顶点和以该顶点为弧尾的所有顶点 。对于各个链表中节点来说,由于表示的都是该顶点的出度或者入度,因此 十字链表中的节点没有先后次序之分

2.2.2 十字链表的 链表节点

  • 十字链表中用于存储顶点的 顶点结构体 节点如下图所示:
    • data :用于存储该顶点中的数据;
    • firstin 指针:用于连接以当前顶点为弧头的其他顶点构成的链表;
    • firstout 指针:用于连接以当前顶点为弧尾的其他顶点构成的链表;
      在这里插入图片描述
  • 十字链表中 弧结构体 节点如下图所示:
    • tailvex: 存储该弧弧尾的顶点位于数组中的下标;
    • headvex:存储该弧弧头的顶点位于数组中的下标;
    • hlink 指针:指向下一个与该弧弧头相同的弧的节点;
    • tlink 指针:指向下一个与该弧弧尾相同的弧的节点;
    • info 指针(可选):用于存储与该顶点相关的信息,例如两顶点之间的权值;
      在这里插入图片描述
  • 例如,用十字链表存储下图 a 中的有向图,存储状态如下图 b 所示:
    • 以顶点 V1 为例,以 V1 为弧头的弧只有 V4→V1。因此,V1 的 firstin 指针指向 V4→V1 这条弧,而在 剩下的弧中没有与 V4→V1 这条弧弧头相同的节点,故 V4→V1 这条弧的 hlink 指针指向空。如图中红色颜色所示。
    • 同样,以 V1 为弧尾的弧有 V1→V2 和 V1→V3(不区分先后次序)这两条弧。因此,V1 的 firstout 指针指向 V1→V2 这条弧的节点, V1→V2 这条弧的 tlink 指针指向 V1→V3 这条弧, V1→V3 这条弧的 tlink 指针指向空。如图中棕色颜色所示。

在这里插入图片描述

2.2.3 十字链表的 C实现

#define  MAX_VERTEX_NUM 20
#define  InfoType int//图中弧包含信息的数据类型
#define  VertexType int

//链表中的其他节点结构体
typedef struct ArcBox 
{
    int tailvex, headvex;//弧尾、弧头对应顶点在数组中的位置下标
    struct ArcBox* hlik, * tlink;//分别指向弧头相同和弧尾相同的下一个弧
    InfoType* info;//存储弧相关信息的指针
}ArcBox;

//链表的首元节点结构体
typedef struct VexNode 
{
    VertexType data;//顶点的数据域
    ArcBox* firstin, * firstout;//指向以该顶点为弧头和弧尾的链表首个结点
}VexNode;

//图结构体
typedef struct 
{
    VexNode xlist[MAX_VERTEX_NUM];//存储顶点的一维数组
    int vexnum, arcnum;//记录图的顶点数和弧数
}OLGraph;

int LocateVex(OLGraph* G, VertexType v)
{
    int i = 0;
    //遍历一维数组,找到变量v
    for (; i < G->vexnum; i++) {
        if (G->xlist[i].data == v) {
            break;
        }
    }
    //如果找不到,输出提示语句,返回 -1
    if (i >= G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}
//构建十字链表函数
void CreateDG(OLGraph* G) {
    //输入有向图的顶点数和弧数
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    //使用一维数组存储各顶点数据,初始化指针域为NULL
    for (int i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->xlist[i].data));
        G->xlist[i].firstin = NULL;
        G->xlist[i].firstout = NULL;
    }
    //构建十字链表
    for (int k = 0; k < G->arcnum; k++) {
        int v1, v2;
        scanf("%d,%d", &v1, &v2);
        //确定v1、v2在数组中的位置下标
        int i = LocateVex(G, v1); //弧的尾的下标
        int j = LocateVex(G, v2); //弧的头的下标  i→j
        //建立弧的其他节点
        ArcBox* p = (ArcBox*)malloc(sizeof(ArcBox));
        p->tailvex = i;
        p->headvex = j;
        //采用头插法插入新的p结点
        p->tlink = G->xlist[i].firstout;
        p->hlik =  G->xlist[j].firstin;
        G->xlist[j].firstin = G->xlist[i].firstout = p;
    }
}

2.3 图的临接多重表存储结构

2.3.1 临接多重表的 基本原理

  • 问题背景
    无向图的存储可以使用邻接表,但在实际使用时,如果想对图中某顶点进行实操(修改或删除),由于邻接表中存储该顶点的节点有两个,因此需要操作两个节点。
    而无向图的另一种存储结构—— 邻接多重表 可以提高无向图中操作顶点的效率
  • 邻接多重表 仅适用于 存储无向的,即无向图或无向网。
  • 邻接多重表存储无向图的方式,可看作是邻接表和十字链表的结合。同邻接表和十字链表存储图的方法相同,都是独自为图中各顶点建立一张链表,存储各顶点的节点作为各链表的首元节点,同时为了便于管理将各个首元节点存储到一个数组中。

2.3.2 临接多重表的 链表节点

  • 邻接多重表采用与邻接表相同的首元节点结构, 顶点结构体 节点 如下图所示:
    • data:存储此顶点的数据;
    • firstedge:指针域,指向同该顶点有直接关联的存储其他顶点的节点。
      在这里插入图片描述
  • 临接多重表的各链表中 边结构体 节点如下图所示:
    • mark:标志域,用于标记此节点是否被操作过,例如在对图中顶点做遍历操作时,为了防止多次操作同一节点,mark 域为 0 表示还未被遍历;mark 为 1 表示该节点已被遍历;
    • ivex 和 jvex:数据域,分别存储图中各边两端的顶点所在数组中的下标;
    • ilink:指针域,指向下一个以 ivex 为顶点的边结构体;
    • jlink:指针域,指向下一个以 jvex 为顶点的边结构体;
    • info:指针域,用于存储与该顶点有关的其他信息,比如无向网中各边的权;
      在这里插入图片描述
  • 如果我们想使用邻接多重表存储下图 a 中的无向图,则与之对应的邻接多重表如下图 b 所示:
    • 以顶点 V1 为例,以 V1 为顶点的边有 V1-V2 和 V1-V4(不区分先后次序)这两条边。因此,V1 的 firstedge 指针指向 V1-V2 这条边的节点, V1-V2 这条边的 ilink 指针指向 V1-V4 这条边的节点, V1-V4 这条边的 ilink 指针指向空。如图中棕色颜色所示。

在这里插入图片描述

2.3.3 临接多重表的 C 实现

#define MAX_VERTEX_NUM 20                   //图中顶点的最大个数
#define VertexType int                      //图顶点的数据类型
#define InfoType int                        //边含有的信息域的数据类型
typedef enum { unvisited, visited }VisitIf; //边标志域

//边结构体
typedef struct EBox {
    VisitIf mark;                  //标志域
    int ivex, jvex;                //边的两个顶点在数组中的位置下标
    struct EBox* ilink, * jlink;   //分别指向与ivex、jvex相关的下一个边
    InfoType* info;                //边包含的其它的信息域的指针
}EBox;

//顶点的首元节点结构体
typedef struct VexBox {
    VertexType data;               //顶点数据域
    EBox* firstedge;               //顶点相关的第一条边的指针域
}VexBox;

//图结构体
typedef struct {
    VexBox adjmulist[MAX_VERTEX_NUM]; //图中顶点的数组
    int vexnum, degenum;              //图中顶点的个数和边的个数
}AMLGraph;

【 3. 图的各存储结构的对比 】

图的存储结构可存储的图的类型核心实现优点缺点
数组有向、无向一维数组存储顶点数据,二维数组存储各顶点间的关系。\\
临接表有向、无向一维数组存储各顶点的结构体(顶点结构体存储该顶点的数据和指向该顶点临接点结构体的指针);
临接点结构体中包括该临接点的数据下标和指向下一个临接点结构体的指针。
\不方便计算有向图中顶点的入度;
不方便操作无向图的顶点
十字链表有向一维数组存储各顶点的结构体(顶点结构体存储各顶点的数据和两个分别指向以该顶点为弧头和弧尾的弧结构体的指针);
弧结构体中存储该弧弧头和弧尾的数据下标、两个分别指向下一个与该弧弧头或弧尾相同的弧的弧结构体。
解决了邻接表不方便计算有向图顶点入度的问题。不能存储无向的
临接多重表无向一维数组存储各顶点的结构体(顶点结构体存储各顶点的数据和指向以该顶点为端点的边结构体的指针);
边结构体存储该边是否被操作过的标记位、该边两个顶点的数据下标、两个分别指向下一个与该边顶点相同的边结构体。
解决了临接表不方便操作无向图顶点的问题。不能存储有向的

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

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

相关文章

IT项目管理【太原理工大学】前置知识点精简总结

根据上次考试以及其他方向考试的经验&#xff0c;这届考试可能偏向出题更灵活&#xff0c;能死记硬背或套公式的题减少&#xff0c;多做准备呀各位大三苦逼人&#xff0c;挂了补考还得回来补考凸^-^凸共勉 &#xff08;另外&#xff0c;别作弊&#xff0c;今天人工智能考试逮住…

mib browser读取mib文件的oid(飞塔防火墙为例)

在配置zabbix监控的时候,配置监控项最为麻烦,一般我们都会套用模板,这种方式比较简单,但是有些设备就是没有现成的zabbix模板,怎么办? 今天我们使用MIB Browser来获取设备SNMP的OID,然后加入zabbix 。 1.什么是MIB Browser SNMP客户端工具MIB Browser, 全名iReasonin…

牛客网刷题 | BC78 KiKi说祝福语

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 2020年来到了&#…

synchronized与volatile关键字

1.synchronized的特性 1.1互斥 synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到 同一个对象 synchronized 就会阻塞等待. 进入 synchronized 修饰的代码块, 相当于 加锁 退出 synchronized 修饰的代码块, 相当于 解锁 syn…

【Linux网络】PXE批量网络装机

目录 一、系统装机 1.1 三种引导方式 1.2 系统安装过程 1.3 四大重要文件 二、PXE 2.1 PXE实现原理 2.2 PXE手动搭建过程 2.3 kickstart配合pxe完成批量自动安装 一、系统装机 1.1 三种引导方式 硬盘光驱(U盘)网络启动 1.2 系统安装过程 加载boot loader加载启动安…

国内SaaS遇冷?未来企业服务赛道是否还有机会?

引言 SaaS 自诞生起&#xff0c;在国内的发展便始终是危与机并存的&#xff0c;虽然使用 SaaS 的企业在逐渐增多&#xff0c;但唱衰 SaaS 的声音也始终不绝于耳。企业付费意愿低&#xff0c;使用产品并不能带来直接的营收提升。SaaS 产品面临着多种不同问题。 在企业对软件付费…

静电纺丝壳聚糖纳米纤维膜

静电纺丝壳聚糖纳米纤维膜是通过静电纺丝技术制备的一种由壳聚糖纳米纤维组成的薄膜材料。静电纺丝技术是一种有效的制备微纳米纤维的方法&#xff0c;可以将高分子溶液或熔体在静电场作用下喷射成纤维状物质&#xff0c;进而形成纳米纤维膜。 壳聚糖是一种天然高分子多糖&…

程序员的实用神器:助力软件开发的利器 ️

程序员的实用神器&#xff1a;助力软件开发的利器 &#x1f6e0;️ 程序员的实用神器&#xff1a;助力软件开发的利器 &#x1f6e0;️引言摘要自动化测试工具&#xff1a;保障代码质量的利剑 &#x1f5e1;️编写高效测试用例 持续集成/持续部署工具&#xff1a;加速交付的利器…

Linux命名管道的创建及应用

目录 一、命名管道的定义即功能 1.1创建命名管道 1.2匿名管道和命名管道的区别 1.3命名管道的打开规则 1.4系统调用unlink 二、进程间命名管道的创建及使用 2.1Comm.hhp 2.2PipeServer.cc 2.3PipeClient.cc 一、命名管道的定义即功能 管道应用的一个限制就是只能在具有…

[XR806开发板试用] XR806 调用cjson 实现数据序列化

很荣幸获得极术设区提供的这次试用机会&#xff0c;可以接触鸿蒙操作系统。我工作接触最多的是linux 平台的嵌入式ARM平台较多&#xff0c;这次跑了下鸿蒙&#xff0c;也非常有趣。 不过接进年底了&#xff0c;日常大小琐碎事情突然多了起来&#xff0c;测评的比较匆忙&#x…

Java的Fork-Join简单介绍

Java的Fork-Join框架是Java 7引入的一个用于并行处理的轻量级框架&#xff0c;它基于分治策略&#xff08;Divide and Conquer&#xff09;&#xff0c;特别适合于那些可以被分解为多个子任务的任务。Fork-Join框架的核心思想是将一个大任务&#xff08;Task&#xff09;拆分成…

为何大学计算机专业以C语言入门?探究C语言入门的好处

为何大学计算机专业以C语言入门&#xff1f;探究C语言入门的好处 在大学计算机专业中&#xff0c;C语言常作为入门语言&#xff0c;这并非偶然。C语言具有一些独特的优势&#xff0c;使其成为计算机科学教育中的理想选择。本文将探讨为何大学计算机专业选择以C语言入门&#xf…

在Unity中实现分页数据显示和分页控制

参考&#xff1a;用两种简单的方式实现unity的分页效果 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.Rendering.VirtualTexturing; using UnityEngine.TerrainUtils;public class PageControll…

简易录制视频做3D高斯

系统环境 ubuntu20 &#xff0c;cuda11.8&#xff0c;anaconda配置好了3D高斯的环境。 具体参考3D高斯环境配置&#xff1a;https://blog.csdn.net/Son_of_the_Bronx/article/details/138527329?spm1001.2014.3001.5501 colmap安装&#xff1a;https://blog.csdn.net/Son_of…

【网络基础2】深入理解TCP协议:协议段、可靠性、各种机制

文章目录 1. TCP协议段格式1.1. 如何解包 / 向上交付1.1.1. 交付1.1.2. 解包 1.2. 如何理解可靠性1.2.1. 确认应答机制&#xff08;ACK&#xff09;1.2.2. 序号 与 确认序号 2. TCP做到全双工的原因2.1. 16位窗口大小2.2. 6个标记位 3. 如何理解连接3.1 连接管理机制3.1.1. 三次…

通俗易懂,Java之Collection接口带你了解集合类型

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

风吸式杀虫灯解析

TH-FD2S风吸式杀虫灯是一种创新且环保的害虫控制设备&#xff0c;它结合了太阳能和风力的双重优势&#xff0c;为农业生产、园林绿化以及居民生活等提供了高效且安全的害虫防治方案。 首先&#xff0c;风吸式杀虫灯的工作原理是利用害虫的趋光性&#xff0c;通过特定的光源吸引…

后仿真中的关于延时问题(物理特性角度)

大家都知道&#xff0c;后仿真讲究仿真时序。那么&#xff0c;在网表阶段&#xff0c;接触到后仿延时问题。今天总结一下。 一 延时概念和分类 1.1 分布式延迟&#xff08;Distributed Delays&#xff09; 一般用来指定模块内部信号通过逻辑单元或者线网耗费的时间。 1.2 模…

【嵌入式必读】一文彻底理解PID自整定及PID自整定代码设计

文章目录 1. 前言2. PID简介3. 常用的PID自整定方法3.1 临界度比例法3.2 衰减曲线法 4. 继电反馈整定法原理4.1 继电反馈自整定的基本思想4.2 继电反馈自整定原理 5. 算法设计5.1 振荡的生成5.2 提取出临界周期 T c T_c Tc​和振荡波形幅值 A A A5.3 计算出PID参数 6 原代码6.1…

SQL Server 存储过程中的字符串本身包含单引号的用法

文章目录 引言I 存储过程中的字符串本身包含单引号的用法1.1 问题1.2解决方法引言 使用场景: 字符串类型字段的值比较 I 存储过程中的字符串本身包含单引号的用法 在SQL Server中,单引号用于表示字符串常量。如果你的存储过程中的字符串本身包含单引号,你需要用两个连续的…