C语言-数据结构 有向图拓扑排序TopologicalSort(邻接表存储)

news2024/9/19 17:33:39

        拓扑排序算法的实现还是比较简单的,我们需要用到一个顺序栈辅助,采用邻接表进行存储,顶点结点存储入度、顶点信息、指向邻接结点的指针,算法过程是:我们先将入度为0的顶点入栈,然后弹出栈顶结点,记录结点个数的count+1,然后遍历所有邻接结点将其入度都减1,如果有入度为0的顶点那么就进栈,当栈不为空就继续循环,最后通过判断弹出栈的元素个数count是否小于全部顶点数,如果小于说明有环,否则无环(即构成拓扑排序)。注意:拓扑排序的情况在邻接表确定的情况下是唯一的。看文字理解确实有点费劲,不过这个实现的代码不难,如果你理解了栈的情况下,那么直接跟着TopologicalSort代码走一遍很快就能领悟到拓扑排序的奥妙!

下面我们将创建一个有向无环图和一个有向有环图

有向无环图如下:

        d6763b7371eb46aeb833bc0a6e510754.png

代码中我们使用头插法进行创建有向无环图邻接表:

#define MAXVEX 8    // 最大顶点数
typedef char VertexType;  // 顶点类型,使用字符表示
typedef int EdgeType;     // 边上的权值类型,使用整数表示
// 边表结点
typedef struct EdgeNode {
    int adjvex;         // 顶点下标,表示该边的终点
    struct EdgeNode* next;  // 指向下一条边的指针
} EdgeNode;

// 顶点结点
typedef struct VertexNode {
    int in;
    VertexType data;     // 顶点数据
    EdgeNode* first;     // 指向该顶点的第一条边
} VertexNode, AdjList[MAXVEX];

// 图的邻接表表示
typedef struct {
    AdjList adjList;    // 顶点数组
    int numNodes;       // 图的顶点数
    int numEdges;       // 图的边数
} GraphAdjList;

//有向无环图邻接表创建
void CreateALGraphNotEncircle(GraphAdjList* G) {
    int i, j;
    EdgeNode* e = NULL;
    char str[] = "ABCDEFGH"; // 顶点数据

    // 初始化邻接表
    for (i = 0; i < G->numNodes; i++) {
        G->adjList[i].data = str[i]; // 设置顶点数据
        G->adjList[i].first = NULL;  // 边表初始化为空
    }
    G->adjList[0].in = 1;
    G->adjList[1].in = 1;
    G->adjList[2].in = 1;
    G->adjList[3].in = 1;
    G->adjList[4].in = 3;
    G->adjList[5].in = 0;
    G->adjList[6].in = 2;
    G->adjList[7].in = 1;
    // 添加边 A->B
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 1; // 邻接顶点序号为B
    e->next = G->adjList[0].first; // 插入到邻接表的第一个位置
    G->adjList[0].first = e;

    // 添加边 B->C->G
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 6; // 邻接顶点序号为G
    e->next = G->adjList[1].first;
    G->adjList[1].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 2; // 邻接顶点序号为C
    e->next = G->adjList[1].first;
    G->adjList[1].first = e;

    // 添加边 C->D
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 3; // 邻接顶点序号为D
    e->next = G->adjList[2].first;
    G->adjList[2].first = e;

    // 添加边 D->H
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 7; // 邻接顶点序号为H
    e->next = G->adjList[3].first;
    G->adjList[3].first = e;

    // 添加边 F->A->E->G
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 6; // 邻接顶点序号为G
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 0; // 邻接顶点序号为A
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    // 添加边 G->E
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[6].first;
    G->adjList[6].first = e;

    // 添加边 H->E
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[7].first;
    G->adjList[7].first = e;

    // 打印邻接表(字母)和入度
    EdgeNode* p = NULL;
    printf("边结点按邻接顶点字母打印 (入度):\n");
    for (i = 0; i < G->numNodes; i++) {
        printf("%c (入度: %d)", G->adjList[i].data, G->adjList[i].in);
        p = G->adjList[i].first;
        while (p != NULL) {
            printf("->%c", G->adjList[p->adjvex].data); // 打印邻接顶点字母
            p = p->next;
        }
        printf("\n");
    }

    // 打印邻接表(下标)和入度
    printf("\n边结点按邻接下标打印 (入度):\n");
    for (i = 0; i < G->numNodes; i++) {
        printf("%c (入度: %d)", G->adjList[i].data, G->adjList[i].in);
        p = G->adjList[i].first;
        while (p != NULL) {
            printf("->%d", p->adjvex); // 打印邻接顶点下标
            p = p->next;
        }
        printf("\n");
    }
}

293e527c7a0e4844ab67b0cddd0a044f.png

        

        有向有环图如下:

52ca7d4673f2431daf71d429b3b21aee.png

代码中我们使用头插法进行创建有向有环图邻接表:

#define MAXVEX 8    // 最大顶点数
typedef char VertexType;  // 顶点类型,使用字符表示
typedef int EdgeType;     // 边上的权值类型,使用整数表示
// 边表结点
typedef struct EdgeNode {
    int adjvex;         // 顶点下标,表示该边的终点
    struct EdgeNode* next;  // 指向下一条边的指针
} EdgeNode;

// 顶点结点
typedef struct VertexNode {
    int in;
    VertexType data;     // 顶点数据
    EdgeNode* first;     // 指向该顶点的第一条边
} VertexNode, AdjList[MAXVEX];

// 图的邻接表表示
typedef struct {
    AdjList adjList;    // 顶点数组
    int numNodes;       // 图的顶点数
    int numEdges;       // 图的边数
} GraphAdjList;

//有向有环图邻接表创建
void CreateALGraphHaveEncircle(GraphAdjList* G) {
    int i, j;
    EdgeNode* e = NULL;
    char str[] = "ABCDEFGH"; // 顶点数据

    // 初始化邻接表
    for (i = 0; i < G->numNodes; i++) {
        G->adjList[i].data = str[i]; // 设置顶点数据
        G->adjList[i].first = NULL;  // 边表初始化为空
    }
    G->adjList[0].in = 1;
    G->adjList[1].in = 1;
    G->adjList[2].in = 1;
    G->adjList[3].in = 1;
    G->adjList[4].in = 3;
    G->adjList[5].in = 1;
    G->adjList[6].in = 2;
    G->adjList[7].in = 1;
    // 添加边 A->B
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 1; // 邻接顶点序号为B
    e->next = G->adjList[0].first; // 插入到邻接表的第一个位置
    G->adjList[0].first = e;

    // 添加边 B->C->G
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 6; // 邻接顶点序号为G
    e->next = G->adjList[1].first;
    G->adjList[1].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 2; // 邻接顶点序号为C
    e->next = G->adjList[1].first;
    G->adjList[1].first = e;

    // 添加边 C->D
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 3; // 邻接顶点序号为D
    e->next = G->adjList[2].first;
    G->adjList[2].first = e;

    // 添加边 D->H
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 7; // 邻接顶点序号为H
    e->next = G->adjList[3].first;
    G->adjList[3].first = e;

    // 添加边 F->A->E

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 0; // 邻接顶点序号为A
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    // 添加边 G->E->F
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 5; // 邻接顶点序号为F
    e->next = G->adjList[6].first;
    G->adjList[6].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[6].first;
    G->adjList[6].first = e;

    // 添加边 H->E
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[7].first;
    G->adjList[7].first = e;

    // 打印邻接表(字母)和入度
    EdgeNode* p = NULL;
    printf("边结点按邻接顶点字母打印 (入度):\n");
    for (i = 0; i < G->numNodes; i++) {
        printf("%c (入度: %d)", G->adjList[i].data, G->adjList[i].in);
        p = G->adjList[i].first;
        while (p != NULL) {
            printf("->%c", G->adjList[p->adjvex].data); // 打印邻接顶点字母
            p = p->next;
        }
        printf("\n");
    }

    // 打印邻接表(下标)和入度
    printf("\n边结点按邻接下标打印 (入度):\n");
    for (i = 0; i < G->numNodes; i++) {
        printf("%c (入度: %d)", G->adjList[i].data, G->adjList[i].in);
        p = G->adjList[i].first;
        while (p != NULL) {
            printf("->%d", p->adjvex); // 打印邻接顶点下标
            p = p->next;
        }
        printf("\n");
    }
}

ed961009ec2d4e2aa72f7a1fd78dbd7f.png

拓扑排序(TopologicalSort)算法,里面包括顺序栈的实现代码非常简洁:

int TopologicalSort(GraphAdjList GL) {
    EdgeNode* e;           // 边节点指针,用于遍历邻接表中的边
    int i, k, gettop;      // 迭代变量和临时变量
    int top = 0;           // 栈顶指针,初始值为0
    int count = 0;         // 已输出的节点计数
    int* stack;            // 存储拓扑排序的栈
    stack = (int*)malloc(sizeof(int) * MAXVEX); // 动态分配栈空间

    // 遍历所有节点,将入度为0的节点入栈
    for (i = 0; i < GL.numNodes; i++) {
        if (0 == GL.adjList[i].in) {
            stack[++top] = i; // 入栈,并更新栈顶指针
        }
    }

    printf("\n拓扑排序序列为:\n");

    // 当栈不为空时,进行拓扑排序
    while (top != 0) {
        gettop = stack[top--]; // 出栈操作,获取栈顶元素,并更新栈顶指针
        printf("%c -> ", GL.adjList[gettop].data); // 打印当前节点的值
        count++; // 已处理节点计数器加1

        // 遍历当前节点的所有邻接节点
        for (e = GL.adjList[gettop].first; e; e = e->next) {
            k = e->adjvex; // 获取邻接节点的索引
            if (!(--GL.adjList[k].in)) { // 将邻接节点的入度减1,并检查是否变为0
                stack[++top] = k; // 如果入度为0,将邻接节点入栈
            }
        }
    }

    // 如果已处理的节点数小于图中节点总数,说明存在环
    if (count < GL.numNodes) {
        return FALSE; // 拓扑排序失败
    }
    return TRUE; // 拓扑排序成功
}

完整代码(有向无环图、有环图的邻接表创建、TopologicalSort算法)

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

#define MAXVEX 8    // 最大顶点数
#define TRUE 1 
#define FALSE 0

typedef char VertexType;  // 顶点类型,使用字符表示
typedef int EdgeType;     // 边上的权值类型,使用整数表示
typedef int Boolean;      // 布尔类型

// 边表结点
typedef struct EdgeNode {
    int adjvex;         // 顶点下标,表示该边的终点
    struct EdgeNode* next;  // 指向下一条边的指针
} EdgeNode;

// 顶点结点
typedef struct VertexNode {
    int in;
    VertexType data;     // 顶点数据
    EdgeNode* first;     // 指向该顶点的第一条边
} VertexNode, AdjList[MAXVEX];

// 图的邻接表表示
typedef struct {
    AdjList adjList;    // 顶点数组
    int numNodes;       // 图的顶点数
    int numEdges;       // 图的边数
} GraphAdjList;


//有向无环图邻接表创建
void CreateALGraphNotEncircle(GraphAdjList* G) {
    int i, j;
    EdgeNode* e = NULL;
    char str[] = "ABCDEFGH"; // 顶点数据

    // 初始化邻接表
    for (i = 0; i < G->numNodes; i++) {
        G->adjList[i].data = str[i]; // 设置顶点数据
        G->adjList[i].first = NULL;  // 边表初始化为空
    }
    G->adjList[0].in = 1;
    G->adjList[1].in = 1;
    G->adjList[2].in = 1;
    G->adjList[3].in = 1;
    G->adjList[4].in = 3;
    G->adjList[5].in = 0;
    G->adjList[6].in = 2;
    G->adjList[7].in = 1;
    // 添加边 A->B
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 1; // 邻接顶点序号为B
    e->next = G->adjList[0].first; // 插入到邻接表的第一个位置
    G->adjList[0].first = e;

    // 添加边 B->C->G
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 6; // 邻接顶点序号为G
    e->next = G->adjList[1].first;
    G->adjList[1].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 2; // 邻接顶点序号为C
    e->next = G->adjList[1].first;
    G->adjList[1].first = e;

    // 添加边 C->D
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 3; // 邻接顶点序号为D
    e->next = G->adjList[2].first;
    G->adjList[2].first = e;

    // 添加边 D->H
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 7; // 邻接顶点序号为H
    e->next = G->adjList[3].first;
    G->adjList[3].first = e;

    // 添加边 F->A->E->G
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 6; // 邻接顶点序号为G
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 0; // 邻接顶点序号为A
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    // 添加边 G->E
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[6].first;
    G->adjList[6].first = e;

    // 添加边 H->E
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[7].first;
    G->adjList[7].first = e;

    // 打印邻接表(字母)和入度
    EdgeNode* p = NULL;
    printf("边结点按邻接顶点字母打印 (入度):\n");
    for (i = 0; i < G->numNodes; i++) {
        printf("%c (入度: %d)", G->adjList[i].data, G->adjList[i].in);
        p = G->adjList[i].first;
        while (p != NULL) {
            printf("->%c", G->adjList[p->adjvex].data); // 打印邻接顶点字母
            p = p->next;
        }
        printf("\n");
    }

    // 打印邻接表(下标)和入度
    printf("\n边结点按邻接下标打印 (入度):\n");
    for (i = 0; i < G->numNodes; i++) {
        printf("%c (入度: %d)", G->adjList[i].data, G->adjList[i].in);
        p = G->adjList[i].first;
        while (p != NULL) {
            printf("->%d", p->adjvex); // 打印邻接顶点下标
            p = p->next;
        }
        printf("\n");
    }
}

//有向有环图邻接表创建
void CreateALGraphHaveEncircle(GraphAdjList* G) {
    int i, j;
    EdgeNode* e = NULL;
    char str[] = "ABCDEFGH"; // 顶点数据

    // 初始化邻接表
    for (i = 0; i < G->numNodes; i++) {
        G->adjList[i].data = str[i]; // 设置顶点数据
        G->adjList[i].first = NULL;  // 边表初始化为空
    }
    G->adjList[0].in = 1;
    G->adjList[1].in = 1;
    G->adjList[2].in = 1;
    G->adjList[3].in = 1;
    G->adjList[4].in = 3;
    G->adjList[5].in = 1;
    G->adjList[6].in = 2;
    G->adjList[7].in = 1;
    // 添加边 A->B
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 1; // 邻接顶点序号为B
    e->next = G->adjList[0].first; // 插入到邻接表的第一个位置
    G->adjList[0].first = e;

    // 添加边 B->C->G
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 6; // 邻接顶点序号为G
    e->next = G->adjList[1].first;
    G->adjList[1].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 2; // 邻接顶点序号为C
    e->next = G->adjList[1].first;
    G->adjList[1].first = e;

    // 添加边 C->D
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 3; // 邻接顶点序号为D
    e->next = G->adjList[2].first;
    G->adjList[2].first = e;

    // 添加边 D->H
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 7; // 邻接顶点序号为H
    e->next = G->adjList[3].first;
    G->adjList[3].first = e;

    // 添加边 F->A->E

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 0; // 邻接顶点序号为A
    e->next = G->adjList[5].first;
    G->adjList[5].first = e;

    // 添加边 G->E->F
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 5; // 邻接顶点序号为F
    e->next = G->adjList[6].first;
    G->adjList[6].first = e;

    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[6].first;
    G->adjList[6].first = e;

    // 添加边 H->E
    e = (EdgeNode*)malloc(sizeof(EdgeNode));
    e->adjvex = 4; // 邻接顶点序号为E
    e->next = G->adjList[7].first;
    G->adjList[7].first = e;

    // 打印邻接表(字母)和入度
    EdgeNode* p = NULL;
    printf("边结点按邻接顶点字母打印 (入度):\n");
    for (i = 0; i < G->numNodes; i++) {
        printf("%c (入度: %d)", G->adjList[i].data, G->adjList[i].in);
        p = G->adjList[i].first;
        while (p != NULL) {
            printf("->%c", G->adjList[p->adjvex].data); // 打印邻接顶点字母
            p = p->next;
        }
        printf("\n");
    }

    // 打印邻接表(下标)和入度
    printf("\n边结点按邻接下标打印 (入度):\n");
    for (i = 0; i < G->numNodes; i++) {
        printf("%c (入度: %d)", G->adjList[i].data, G->adjList[i].in);
        p = G->adjList[i].first;
        while (p != NULL) {
            printf("->%d", p->adjvex); // 打印邻接顶点下标
            p = p->next;
        }
        printf("\n");
    }
}

int TopologicalSort(GraphAdjList GL) {
    EdgeNode* e;           // 边节点指针,用于遍历邻接表中的边
    int i, k, gettop;      // 迭代变量和临时变量
    int top = 0;           // 栈顶指针,初始值为0
    int count = 0;         // 已输出的节点计数
    int* stack;            // 存储拓扑排序的栈
    stack = (int*)malloc(sizeof(int) * MAXVEX); // 动态分配栈空间

    // 遍历所有节点,将入度为0的节点入栈
    for (i = 0; i < GL.numNodes; i++) {
        if (0 == GL.adjList[i].in) {
            stack[++top] = i; // 入栈,并更新栈顶指针
        }
    }

    printf("\n拓扑排序序列为:\n");

    // 当栈不为空时,进行拓扑排序
    while (top != 0) {
        gettop = stack[top--]; // 出栈操作,获取栈顶元素,并更新栈顶指针
        printf("%c -> ", GL.adjList[gettop].data); // 打印当前节点的值
        count++; // 已处理节点计数器加1

        // 遍历当前节点的所有邻接节点
        for (e = GL.adjList[gettop].first; e; e = e->next) {
            k = e->adjvex; // 获取邻接节点的索引
            if (!(--GL.adjList[k].in)) { // 将邻接节点的入度减1,并检查是否变为0
                stack[++top] = k; // 如果入度为0,将邻接节点入栈
            }
        }
    }

    // 如果已处理的节点数小于图中节点总数,说明存在环
    if (count < GL.numNodes) {
        return FALSE; // 拓扑排序失败
    }
    return TRUE; // 拓扑排序成功
}


int main() {
    GraphAdjList GL;
    GL.numNodes = MAXVEX;        // 设置顶点数
    //情况1有向无环图
    printf("测试有向无环图是否构成拓扑排序\n\n");
    CreateALGraphNotEncircle(&GL);
    if (TopologicalSort(GL)) {
        printf("\n能构成拓扑排序,图中无环\n");
    }
    else {
        printf("\n不能构成拓扑排序,图中有环\n");
    }printf("\n");
    //情况2有向有环图
    printf("测试有向有环图是否构成拓扑排序\n\n");
    CreateALGraphHaveEncircle(&GL);
    if (TopologicalSort(GL)) {
        printf("\n能构成拓扑排序,图中无环\n");
    }
    else {
        printf("\n不能构成拓扑排序,图中有环\n");
    }
    return 0;
}

运行结果:

有向无环图

ee7dd2f22d7c4bb78fb51476cf1f07f9.png

有向有环图(A->B->G->F是环)

c508b76c97c448eea47463546a49fd41.png

b2dd2e7a7b1544c68983f08af08cbfea.png

 

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

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

相关文章

使用CUBE_MX使用I2C通信,实现对EEPROM的读写

一、使用CUBE_MX配置 1.配置I2C 2.配置USART1 3.重中之重(在KEIL5打开串口使用的库) 二、KEIL5配置 #include "main.h" #include "i2c.h" #include "gpio.h" #include "usart.h"#include <stdio.h>void SystemClock_Config(vo…

flash_attention简要笔记

优化效果 原来&#xff0c;attention部分的计算量和中间激活占用显存的复杂度都是 O ( N 2 ) O(N^2) O(N2) 计算量部分原来QK矩阵乘和attn_scoreV矩阵乘的计算量&#xff0c;复杂度都是 O ( N 2 ) O(N^2) O(N2)&#xff1b;中间激活因为中间有一个attn_score&#xff0c;所以复…

如何接口对接发送视频短信

随着移动通信技术的飞速发展&#xff0c;视频短信作为一种创新的多媒体信息传递方式&#xff0c;正逐渐成为众多行业不可或缺的沟通工具。它不仅丰富了信息传递的形式&#xff0c;还显著提高了信息接收者的参与度和满意度。 支持免费对接试用乐讯通PaaS平台 找好用的短信平台,选…

数据结构:(OJ141)环形列表

给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;…

探索iPhone一键删除重复照片的方法

在iPhone用户的生活中&#xff0c;存在一个不变的真理&#xff1a;不管你的照片库有多干净&#xff0c;重复的照片总会找到一种方法悄无声息地积累起来&#xff0c;就像袜子在洗衣机中神秘消失那样不可思议。而当你最终决定处理这些重复照片时&#xff0c;你可能已经面临着一个…

Electron 图标修改

目录 1. 图片基本要求 2. 在main.js中配置icon 位置 ​3. 在package.json 中配置icon 位置 4. 问题&#xff1a;左上角图片 开发环境下显示&#xff0c;生产环境下不显示 1. 图片基本要求 图片格式为ico&#xff0c;图片像素像素为256*256&#xff1b; 将ico文件放在pub…

基于Springboot的医疗健康助手开题报告

文未可获取一份本项目的java源码和数据库参考。 一&#xff0e;选题意义, 研究现状,可行性分析 选题意义&#xff1a;随着科技的高速发展&#xff0c;人们的生活水平也正在稳步提高&#xff0c;解决温饱问题以后&#xff0c;广大人民群众也越来越注重自己的身体健康&#xff0…

openGauss 基于PITR的恢复

作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯及Greenplum备份恢复&#xff0c; 安装迁移&#xff0c;性能优化、故障…

聚焦汽车智能化与电动化,亚洲领先的汽车工业技术博览会 2025年11月与您相约 AUTO TECH 华南展

抢占市场先机︱聚焦汽车智能化与电动化&#xff0c;亚洲领先的汽车工业技术博览会 2025年11月与您相约 AUTO TECH 华南展 随着汽车智能化与电动化的迅猛发展&#xff0c;汽车电子技术、车用功率半导体技术、智能座舱技术、轻量化技术/材料、软件定义汽车、EV/HV技术、测试测量技…

python之openpyxl模块——实现Excel表格的处理(万字教学,全网最全,超详细!)

文章目录 前言1、Excel表格介绍扩展&#xff1a;.xls 和 .xlsx 2、openpyxl 模块2.1 模块的安装2.2 基础操作2.2.1 生成Excel文件对象&#xff0c;查看所有sheet表2.2.2 通过表名得到表对象2.2.3 获取活动表对象2.2.4 获取表格中数据所占大小2.2.5 获取单元格中的数据2.2.6 获取…

flask搭建微服务器并训练CNN水果识别模型应用于网页

一. 搭建flask环境 概念 flask:一个轻量级 Web 应用框架&#xff0c;被设计为简单、灵活&#xff0c;能够快速启动一个 Web 项目。CNN:深度学习模型&#xff0c;用于处理具有网格状拓扑结构的数据&#xff0c;如图像&#xff08;2D网格&#xff09;和视频&#xff08;3D网格&a…

集成学习详细介绍

以下内容整理于&#xff1a; 斯图尔特.罗素, 人工智能.现代方法 第四版(张博雅等译)机器学习_温州大学_中国大学MOOC(慕课)XGBoost原理介绍------个人理解版_xgboost原理介绍 个人理解-CSDN博客 集成学习(ensemble)&#xff1a;选择一个由一系列假设h1, h2, …, hn构成的集合…

LLM大模型基础知识学习总结,零基础入门到精通 非常详细收藏我这一篇就够了

在这个已经被大模型包围的时代&#xff0c;不了解一点大模型的基础知识和相关概念&#xff0c;可能出去聊天都接不上话。刚好近期我也一直在用ChatGPT和GitHub Copilot&#xff0c;也刚好对这些基础知识很感兴趣&#xff0c;于是看了一些科普类视频和报告&#xff0c;做了如下的…

从数据到决策,无限住宅代理还可以这么用

在企业发展中&#xff0c;一个良好的决策可以起到推波助澜的作用&#xff0c;让企业飞速发展。在决策的背后离不开数据的支撑&#xff0c;数据驱动决策已成为企业成功的关键因素。然而&#xff0c;随着数据量的激增和竞争的加剧&#xff0c;企业如何有效地收集、分析和应用数据…

Python 课程14-TensorFlow

前言 TensorFlow 是由 Google 开发的一个开源深度学习框架&#xff0c;广泛应用于机器学习和人工智能领域。它具有强大的计算能力&#xff0c;能够运行在 CPU、GPU 甚至 TPU 上&#xff0c;适用于从小型模型到大规模生产系统的各种应用场景。通过 TensorFlow&#xff0c;你可以…

【云原生监控】Prometheus之Alertmanager报警

Prometheus之Alertmanager报警 文章目录 Prometheus之Alertmanager报警概述资源列表基础环境一、部署Prometheus服务1.1、解压1.2、配置systemctl启动1.3、监控端口 二、部署Node-Exporter2.1、解压2.2、配置systemctl启动2.3、监听端口 三、配置Prometheus收集Exporter采集的数…

旧衣回收小程序:开启旧衣回收新体验

随着社会的大众对环保的关注度越来越高&#xff0c;旧衣物回收市场迎来了快速发展时期。在数字化发展当下&#xff0c;旧衣回收行业也迎来了新的模式----互联网旧衣回收小程序&#xff0c;旨在为大众提供更加便捷、简单、透明的旧衣物回收方式&#xff0c;通过手机直接下单&…

关于1688跨境官方接口的接入||跨境卖家必知的1688跨境要点

1688跨境是什么&#xff1f; 1688是国内领先的货源平台&#xff0c;每年服务超过6500万B类买家&#xff0c;其中很大一部分是跨境商家。这些跨境商家采购中国高性价比的商品到海外销售。 为什么要入驻跨境专供&#xff1f; 据统计&#xff0c;2028年跨境市场规模将实现翻三番&…

RabbitMQ(高阶使用)延时任务

文章内容是学习过程中的知识总结&#xff0c;如有纰漏&#xff0c;欢迎指正 文章目录 1. 什么是延时任务&#xff1f; 1.1 和定时任务区别 2. 延时队列使用场景 3. 常见方案 3.1 数据库轮询 优点 缺点 3.2 JDK的延迟队列 优点 缺点 3.3 netty时间轮算法 优点 缺点 3.4 使用消息…

HTML5好看的水果蔬菜在线商城网站源码系列模板2

文章目录 1.设计来源1.1 主界面1.2 商品列表界面1.3 商品详情界面1.4 其他界面效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/142059220 HTML5好看的水果蔬菜在线商城…