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

文章目录

  • 【 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.mfbz.cn/a/600345.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Fastadmin】后台角色组权限问题(multi,开关switch,控制器新增方法)

1.列表开关类型的权限 如图&#xff1a; 此类开关请求的方法为multi 开关在点击的时候默认是只允许修改数据库的status字段的&#xff0c;如果我们开关不是status字段&#xff0c;我们需要在服务端对应的控制器中定义protected $multiFields"id,name,swith";&#x…

一个物业管理服务项目的思考——智慧停车场无人值守呼叫系统到电梯五方对讲再到呼叫中心

目录 起源智慧停车场无人值守呼叫系统然后电梯五方对讲系统又然后物业呼叫中心集控E控中心怎么做 起源 小区里新装了智慧停车场系统&#xff0c;马上展现出了科技化、现代化的新形象。一个显著的好处是&#xff1a;停车场的出入口&#xff0c;再也看不到司机和保安争吵的场景了…

四川景源畅信:抖音的运营策略有哪些?

在数字营销的大潮中&#xff0c;抖音以其巨大的用户基础和强大的传播力成为众多品牌和商家的必争之地。那么&#xff0c;抖音的运营策略有哪些呢?这个问题涉及到内容创作、用户互动、数据分析和品牌合作等多个方面。 一、内容创作与优化在抖音&#xff0c;内容是吸引用户的关键…

C++ 数据内存分布揭秘:从栈到堆的探索之旅

目录 1. 栈(Stack) 2. 堆(Heap) malloc和new的区别 堆与栈在C中的异同点详解 3. 数据段(Data Segment) 4. 代码段(Code Segment) 5. 动态内存分配的陷阱 当我们谈论C编程时&#xff0c;对内存布局的理解至关重要。本文将深入探讨C中各种变量和数据结构在内存中的分布情况…

这些CTF,不仅学技术,还有巨额奖金!

前言&#xff1a; 不会吧&#xff0c;不会吧&#xff0c;不会还有安全er不知道CTF是什么吧&#xff1f; 在程序员的世界里&#xff0c;也有ACM这样的编程大赛&#xff0c;成为各路编程高手一较高下展示能力的平台。 那在网络安全的圈子里&#xff0c;各路黑客红客白帽子们又…

rag-embeddings基础流程

什么是检索增强的生成模型 LLM 固有的局限性 LLM 的知识不是实时的LLM 可能不知道你私有的领域/业务知识 检索增强生成 RAG&#xff08;Retrieval Augmented Generation&#xff09;顾名思义&#xff0c;通过检索的方法来增强生成模型的能力。 类比&#xff1a;你可以把这个…

linux 内核编译

目录 Linux操作系统框架 Linux内核的主要功能&#xff1a; Linux的内核目录结构&#xff1a; 结构图: 详细介绍&#xff1a; uname - a 补充 编译之前 UTC 时间补充 Linux内核编译流程: 方法一: 官方内核编译: 1. 运行 build.sh 脚本&#xff0c; 记得加 sudo 权…

Day 24 数据库管理及数据类型

数据库管理及数据类型 一&#xff1a;数据类型 1.数值类型 整数类型 ​ 整数类型&#xff1a;TINYINT SMALLINT MEDIUMINT INT BIGINT ​ 作用&#xff1a;用于存储用户的年龄、游戏的Level、经验值等 浮点数类型 ​ 浮点数类型&#xff1a;FLOAT DOUBLE ​ 作用&#xf…

linux或ubuntu环境下需要自行安装vivado USB Program下载程序驱动

如果在linux或ubuntu环境下&#xff0c;不安装驱动是无法下载FPGA程序的。在linux或ubuntu环境下安装程序不要自动安装。 johnjohn-wang:~/vitis2021.2/Vivado/2021.2/data/xicom/cable_drivers/lin64/install_script/install_drivers$ sudo ./install_drivers

1天搞定SpringBoot+Vue全栈开发 (7)Axios网络请求

1.Axios的使用 Axios中文文档 | Axios中文网Axios 是一个基于 promise 的网络请求库&#xff0c;可以用于浏览器和 node.jshttps://www.axios-http.cn/ 2.与vue整合 App.vue: <template><div id"app"><Moviev-for"movie in movies":key&qu…

一致性评价政策加速行业仿制药洗牌,惯爱为代表的新锐品牌崭露头角

从印度神油到以形补形&#xff0c;男人的问题&#xff0c;从古至今一直困扰着很多人&#xff0c;大多人都羞于启齿。然而&#xff0c;沉默的背后&#xff0c;隐藏着令人震惊的数据&#xff1a;据统计显示&#xff0c;ED&#xff08;勃起功能障碍&#xff09;是男性生殖系统发病…

9.Java内置锁的核心原理-Synchronized

文章目录 Java内置锁的核心原理-Synchronized1.线程安全问题1.1.自增运算分析1.2.临界区资源和临界区代码片段 2.synchronized关键字2.1.synchronized同步方法2.2.synchronized同步代码块2.3.synchronized同步方法和synchronized同步代码块区别2.4.静态的同步方法2.5.内置锁的释…

18、ESP32 ESP-NOW 点对点通信

ESP-NOW 是乐鑫自主研发的无连接通信协议&#xff0c;具有短数据包传输功能。该协议使多个设备能够以简单的方式相互通信。 ESP-NOW 功能 ESP-NOW 支持以下功能&#xff1a; 加密和未加密的单播通信;混合加密和未加密的对等设备;最多可携带 250 字节 的有效载荷;发送回调功能…

C#修改默认参数settings文件

右击项目在设置中进行修改&#xff1a; 千万不要在这里改。 如果要在自己的项目里添加这个文件&#xff0c;首先新建个文件夹&#xff0c;然后添加.setting文件&#xff0c;然后再像上面说的那样添加属性。

通过 Java 操作 redis -- String 基本命令

关于 redis String 类型的相关命令推荐看 Redis - String 字符串 要想通过 Java 操作 redis&#xff0c;首先要连接上 redis 服务器&#xff0c;推荐看通过 Java 操作 redis -- 连接 redis 本博客只介绍了一小部分常用的命令&#xff0c;其他的命令根据上面推荐的博客也能很简单…

Penpad再获 Presto Labs 投资,Scroll 生态持续扩张

​Penpad 是 Scroll 生态的 LaunchPad 平台&#xff0c;其整计划像收益聚合器以及 RWA 等功能于一体的综合性 Web3 平台拓展&#xff0c;该平台在近期频获资本市场关注&#xff0c;并获得了多个知名投资者/投资机构的支持。 截止到本文发布前&#xff0c;Penpad 已经获得了包括…

【免费】虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型【simulink】

目录 主要内容 仿真模型要点 2.1 整体仿真模型 2.2 电压电流双闭环模块 2.3 SVPWM调制策略 2.4 无功电压模块 2.5 自适应控制策略及算法 部分结果 下载链接 主要内容 该模型为simulink仿真模型&#xff0c;主要实现的内容如下&#xff1a; 随着风力发电、光…

4.请求体

什么是请求体(Request Body) 请求体是客户端发送到API的数据。 响应体是API发送给客户端的数据 API几乎总是必须发送一个响应体&#xff0c;但是客户端并不需要一直发送请求体 定义请求体&#xff0c;需要使用 Pydantic 模型 不能通过GET请求发送请求体发送请求体数据&…

ISIS的工作原理

1.邻居关系建立 &#xff08;1&#xff09;IS-IS领接关系建立原则 1、通过将以太网接口模拟成点到点接口&#xff0c;可以建立点到点链路邻接关系。 2、当链路两端IS-IS接口的地址不在同一网段时&#xff0c;如果配置接口对接收的Hello报文不作IP地址检查&#xff0c;也可以建…

基于Springboot的教学辅助系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的教学辅助系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…
最新文章