最小代价生成树实现(算法与数据结构设计)

news2025/6/9 14:06:21

课题内容和要求

最小代价生成树的实现,分别以普利姆算法和克鲁斯卡尔算法实现最小代价生成树,并分析两种算法的适用场合。

数据结构说明

普利姆算法实现最小代价生成树的图采用邻接表存储结构,还有辅助数据结构,数组nearest,数组lowcost,数组mark。克鲁斯卡尔算法实现最小代价生成树的图采用邻接矩阵存储结构,其中还定义了辅助数据结构用于存放边。

算法设计

普利姆算法。
辅助数据结构说明:
(1)一维数组nearest,nearest[v]存放与v距离最近的顶点编号u,距离最近是指边(u,v)是所有与顶点v关联的边中权值最小的边。初始时,nearest[v] = -1.
(2)一维数组lowcost,lowcost[v]用于存放边(nearest[v],v)的权值。初始时lowcost[v]=INFINITY。INFINITY是假定的极大值。
(3)一维数组mark,mark[v]用于标记顶点v是否在生成树中,若mark[v]=0,表示顶点v未加入生成树;否则,表示v已加入生成树。初始时,mark[v]=0。
算法步骤:
(1)分别将数组nearest,lowcost,mark初始化并将源点加入生成树。
(2)循环n-1次,重复以下操作。
a.查找顶点k的未加入生成树的所有邻接顶点j,若边(k,j)的权值比lowcost[j]小,则将lowcost[j]更新为此权值,并令lowcost[j]=k。
b.在未加入生成树的顶点中,查找具有最小lowcost的顶点k。
c.将k加入生成树。

克鲁斯卡尔算法。
辅助数据结构说明:
(1)一维数组Edge,从邻接矩阵中获取所有边以及边对应的顶点保存在数组Edge中,并且采用排序算法对边按照权值递增排序。
(2)一维数组parent,用于标识各个顶点所属的连通分量,若两个顶点属于不同的连通分量,则将这两个顶点关联的边加入到生成树中。parent[i]表示顶点i所属的连通分量,初始时parent[i]=i,表示各顶点自成一个连通分量。
算法步骤:
(1)从邻接矩阵中获取所有边存储于数组Edge。
(2)调用快速排序对数组Edge边按权值从小到大排序
(3)变量k表示当前构造的最小代价生成树中的边数,其初始值为0,若k<n-1,则循环执行以下操作。
a.依次从Edge中取出边(u,v)
对u和v所在连通分量paren[u]和parent[v]进行判断,若两者不相等,表示两顶点属于不同连通分量,输出此边,并合并paren[u]和parent[v]两个连通分量;若paren[u]和parent[v]相等,表示属于同一连通分量,舍去此边而选择下一条最小的边。

详细设计

普利姆算法生成最小代价生成树文件Prim_main.c、Prim.c、Prim.h
Prim_main.c

#include <stdio.h>
#include <stdlib.h>
#include "Prim.h"
int main()
{
    // 以下为图例1
    LGraph *lg = (LGraph *)malloc(sizeof(LGraph));
    if (!Init(lg, 6))
    {
        printf("邻接表初始化失败\n");
        return 0;
    }
    // 定义边集数组然后利用循环加入到图中
    int e[18][3] = {{0, 1, 10}, {1, 0, 10}, {1, 4, 2}, {4, 1, 2}, {4, 3, 9}, 
                    {3, 4, 9}, {0, 5, 4}, {5, 0, 4}, {4, 5, 7}, {5, 4, 7}, 
                    {0, 2, 7}, {2, 0, 7}, {2, 5, 6}, {5, 2, 6}, {3, 5, 5}, 
                    {5, 3, 5}, {2, 3, 8}, {3, 2, 8}};
    for (int i = 0; i < 18; i++)
    {
        if (!Insert(lg, e[i][0], e[i][1], e[i][2]))
        {
            printf("边插入失败\n");
            return 0;
        }
    }
    int nearest[6] = {0};
    int lowcost[6] = {0};
    if (!Prim(0, nearest, lowcost, *lg))    // 构造最小生成树,以结点0为源点
    {
        printf("最小生成树构造失败\n");
    }
    printf("图例1最小代价生成树的边集如下:\n");
    int sum = 0;    // 计算最小权值之和
    for (int i = 0; i < lg->n; i++) // 输出最小代价生成树的边集
    {
        printf("(%d,%d,%d) ",nearest[i],i,lowcost[i]);
        sum+=lowcost[i];
    }
    printf("\n最小权值和为%d\n",sum);
    // 以上为图例1
    printf("----------------------------\n");
    // 以下为图例2
    LGraph *lg_2 = (LGraph *)malloc(sizeof(LGraph));
    if (!Init(lg_2, 6))
    {
        printf("邻接表初始化失败\n");
        return 0;
    }
    // 定义边集数组然后利用循环加入到图中
    int e_2[20][3] = {{0, 1, 6}, {1, 0, 6}, {0, 2, 1}, {2, 0, 1}, {0, 3, 5}, 
                    {3, 0, 5}, {1, 2, 5}, {2, 1, 5}, {2, 3, 5}, {3, 2, 5}, 
                    {1, 4, 3}, {4, 1, 3}, {3, 5, 2}, {5, 3, 2}, {2, 4, 6}, 
                    {4, 2, 6}, {2, 5, 4}, {5, 2, 4}, {4, 5, 6}, {5, 4, 6}};
    for (int i = 0; i < 20; i++)
    {
        if (!Insert(lg_2, e_2[i][0], e_2[i][1], e_2[i][2]))
        {
            printf("边插入失败\n");
            return 0;
        }
    }
    int nearest_2[6] = {0};
    int lowcost_2[6] = {0};
    if (!Prim(0, nearest_2, lowcost_2, *lg_2))  // 构造最小生成树,以0为源点
    {
        printf("最小生成树构造失败\n");
    }
    printf("图例2最小代价生成树的边集如下:\n");
    sum = 0;
    for (int i = 0; i < lg_2->n; i++)   // 输出最小代价生成树的边集
    {
        printf("(%d,%d,%d) ",nearest_2[i],i,lowcost_2[i]);
        sum+=lowcost_2[i];
    }
    printf("\n最小权值和为%d\n",sum);
    printf("输入回车结束程序。\n");
    getchar();
    Destroy(lg);
    Destroy(lg_2);
}

Prim.h

#ifndef __PRIM_H__
#define __PRIM_H__
typedef struct eNode    // 边结点
{
    int adjVex;            // 顶点v
    int w;                 // 点u与点v之间的权值
    struct eNode *nextArc; // 指向下一个结点
} ENode;
typedef struct lGraph   // 邻接表
{
    int n; // 图顶点数
    int e; // 图边数
    ENode **a;  // 指向一维指针的数组
} LGraph;
#define INFINITY 65535  // 假定一个无限值

int Init(LGraph *lg, int nSize);                       // 邻接表初始化
void Destroy(LGraph *lg);                              // 邻接表的撤销
int Exist(LGraph *lg, int u, int v);                   // 边搜索
int Insert(LGraph *lg, int u, int v, int w);           // 边插入
int Prim(int k, int *nearest, int *lowcost, LGraph g); // 普利姆算法
#endif

Prim.c

#include "Prim.h"
#include <stdlib.h>
#include <stdio.h>
/**
 * @brief 邻接表初始化
 * @param lg 指向邻接表的指针
 * @param nSize 顶点个数
 * @return 0,初始化失败;1,初始化成功
*/
int Init(LGraph *lg, int nSize)
{
    lg->n = nSize;
    lg->e = 0;
    lg->a = (ENode **)malloc(nSize * sizeof(ENode *)); // 动态生成长度为n的一维指针数组
    if (!lg->a)   // 判断空间申请是否成功
    {
        return 0; // 申请空间失败
    }
    else
    {
        for (int i = 0; i < lg->n; i++)
        {
            lg->a[i] = NULL; // 将指针数组a置空
        }
        return 1; // 申请空间成功
    }
}
/**
 * @brief 邻接表的撤销
 * @param lg 指向邻接表的指针
 * @return void
*/
void Destroy(LGraph *lg)
{
    int i;
    ENode *p,*q;
    for(i = 0;i<lg->n;i++)
    {
        p = lg->a[i];
        q=p;
        while(p)
        {
            p = p->nextArc;
            free(q);
            q=p;
        }
    }
    free(lg->a);
}
// 边搜索
/**
 * @brief 边搜索
 * @param lg 指向邻接表的指针
 * @param u 边的始点
 * @param v 边的终点
 * @return 0,搜索失败或顶点有误;1,搜索成功
*/
int Exist(LGraph *lg, int u, int v)
{
    ENode *p = NULL;
    if(u<0||v<0||u>lg->n-1||v>lg->n-1||u==v)
    {

        return 0;  // 判断顶点是否有误
    }
    p=lg->a[u];
    while(p&&p->adjVex != v)
    {
        p = p->nextArc;
    }
    if(!p)
    {
        return 0;  // 未找到边
    }
    else
    {
        return 1;   // 找到边
    }
}
// 边插入
/**
 * @brief 边插入
 * @param lg 指向邻接表的指针
 * @param u 边的始点
 * @param v 边的终点
 * @param w 边的权值
 * @return 0,插入失败或边已存在;1,插入成功
*/
int Insert(LGraph *lg, int u, int v, int w)
{
    ENode *p = NULL;
    if(u<0||v<0||u>lg->n-1||v>lg->n-1||u==v)
    {

        return 0;  // 判断顶点是否有误
    }
    if(Exist(lg,u,v))
    {
        printf("边已存在。\n");
        return 0;
    }
    p = (ENode*)malloc(sizeof(ENode));  // 为新结点分配空间
    p->adjVex = v;
    p->w = w;
    
    // 将新结点插入单链表的最前面,头插
    p->nextArc = lg->a[u];  
    lg->a[u] = p;
    
    lg->e++;
    return 1;
}
// 普利姆算法
/**
 * @brief 普利姆算法生成最小代价生成树
 * @param k 源点
 * @param nearest 存放顶点的最近顶点编号
 * @param lowcost 存放顶点距离最近点的距离
 * @param g 邻接表
 * @return 0,失败;1,成功
*/
int Prim(int k, int *nearest, int *lowcost, LGraph g)
{
    ENode *p;
    int i,j;
    int *mark = (int*)malloc(g.n*sizeof(int));  // 辅助数组
    if(k<0||k>g.n-1)    // 判断源点是否有效
    {
        return 0;
    }
    for(i = 0;i<g.n;i++)    // nearest数组,lowcost数组和mark数组初始化
    {
        nearest[i] = -1;
        lowcost[i] = INFINITY;
        mark[i]=0;  // 赋值为1表示结点加入生成树,0表示未加入
    }

    // 源点k加入生成树
    lowcost[k] = 0;
    nearest[k]=k;
    mark[k] = 1;
    
    for(i = 1;i<g.n;i++)    // 将其余n-1条边加入生成树
    {
        for(p = g.a[k];p;p = p->nextArc)    // 更新生成树外的顶点lowcost值
        {
            j=p->adjVex;
            if((!mark[j])&&(lowcost[j]>p->w))
            {
                lowcost[j] = p->w;
                nearest[j] = k;
            }
        }
        int min = INFINITY;
        for(j = 0;j<g.n;j++)    // 找生成树外顶点中具有最小lowcost值的顶点k
        {
            if((!mark[j])&&(lowcost[j]<min))
            {
                min = lowcost[j];
                k = j;
            }
        }
        mark[k]=1;  // 将顶点k加入生成树
    }
    return 1;
}

克鲁斯卡尔算法生成最小代价生成树文件Kruskal_main.c、Kruskal.h、Kruskal.c
Kruskal_main.c

#include <stdio.h>
#include <stdlib.h>
#include "Kruskal.h"
int main()
{
    // 图例1
    MGraph *mg = (MGraph*)malloc(sizeof(MGraph));
    if(!Init(mg,6))
    {
        printf("邻接矩阵初始化失败\n");
    }
    // 定义边集数组然后利用循环加入到图中
    int e[9][3] = {{0,1,10},{1,4,2},{0,2,7},{0,5,4},{4,5,7},
                        {3,4,9},{2,5,6},{3,5,5},{2,3,8}};
    
    for(int i = 0;i<9;i++)
    {
        if(!Insert(mg,e[i][0],e[i][1],e[i][2]))
        {
            printf("边插入失败\n");
        }
    }
    Edge *edg = (Edge*)malloc(mg->e*sizeof(Edge));  // 辅助数据结构,存放边集
    int k = 0;
    for(int i = 0;i<mg->n;i++)
    {
        for(int j = 0; j<i;j++)
        {
            if(mg->a[i][j]!=0&&mg->a[i][j]!=INFINITY)
            {
                edg[k].u = i;
                edg[k].v = j;
                edg[k].w = mg->a[i][j];
                k++;
            }
        }
    }
    int sum = 0;    // 计算最小代价生成树权值和
    printf("图例1最小代价生成树的边集如下:\n");
    Kruskal(mg,edg,&sum);
    printf("\n最小代价生成树权值和为:%d\n",sum);
    // 以上为图例1
    printf("-------------------------\n");
    // 以下为图例2
    MGraph *mg_2 = (MGraph*)malloc(sizeof(MGraph));
    if(!Init(mg_2,6))
    {
        printf("邻接矩阵初始化失败\n");
    }
    // 定义边集数组然后利用循环加入到图中
    int e_2[10][3] = {{0,1,6},{0,2,1},{0,3,5},{1,2,5},{2,3,5},
                    {1,4,3},{2,4,6},{2,5,4},{3,5,2},{4,5,6}};
    for(int i = 0;i<10;i++)
    {
        if(!Insert(mg_2,e_2[i][0],e_2[i][1],e_2[i][2]))
        {
            printf("边插入失败\n");
        }
    }
    Edge *edg_2 = (Edge*)malloc(mg_2->e*sizeof(Edge));
    k = 0;
    for(int i = 0;i<mg_2->n;i++)
    {
        for(int j = 0; j<i;j++)
        {
            if(mg_2->a[i][j]!=0&&mg_2->a[i][j]!=INFINITY)
            {
                edg_2[k].u = i;
                edg_2[k].v = j;
                edg_2[k].w = mg_2->a[i][j];
                k++;
            }
        }
    }
    sum = 0;    // 计算最小代价生成树权值和
    printf("图例2最小代价生成树的边集如下:\n");
    Kruskal(mg_2,edg_2,&sum);
    printf("\n最小代价生成树权值和为:%d\n",sum);
    printf("输入回车结束程序。\n");
    getchar();
    Destroy(mg);
    Destroy(mg_2);
}

Kruskal.h

#ifndef __KRUSKAL_H__
#define __DRUSKAL_H__
typedef struct mGraph
{
    int **a; // 邻接矩阵
    int n;   // 图的顶点数
    int e;   // 图的边数
} MGraph;
typedef struct edge // 辅助数据结构,保存邻接矩阵的边
{
    int u;  // 边的始点
    int v;  // 边的终点
    int w;  // 边的权值
} Edge;
#define INFINITY 65535  // 假定一个最大值,给边赋值表示顶点间不可达
int Init(MGraph *mg, int nSize);                // 邻接矩阵初始化
void Destroy(MGraph *mg);                       // 邻接矩阵的撤销
int Insert(MGraph *mg, int u, int v, int w);    // 边插入
int FindParent(int *parent, int a);             // 查结点树根
void Swap(Edge *edg, int i, int j);             // 交换函数
int Partition(Edge *edg, int left, int right);  // 分划函数
void QuickSort(Edge *edg, int left, int right); // 快速排序
void Kruskal(MGraph *mg, Edge *edg,int *sum);            // 克鲁斯卡尔算法生成最小代价生成树
#endif

Kruskal.c

#include <stdio.h>
#include <stdlib.h>
#include "Kruskal.h"
/**
 * @brief 邻接矩阵初始化
 * @param mg 指向待初始化的矩阵指针
 * @param nSize 顶点数
 * @return 0,初始化失败;1,初始化成功
*/
int Init(MGraph *mg, int nSize)
{
    mg->n = nSize;  // 初始化顶点数
    mg->e = 0;
    mg->a = (int**)malloc(nSize*sizeof(int*));
    if(!mg->a)
    {
        return 0;   // 初始化失败
    }
    for(int i = 0;i<mg->n;i++)  // 循环初始化每条边
    {
        mg->a[i] = (int*)malloc(nSize*sizeof(int));
        for(int j = 0; j<mg->n; j++)
        {
            mg->a[i][j] = INFINITY;
        }
        mg->a[i][i] = 0;
    }
    return 1;   // 初始化成功
}
/**
 * @brief 邻接矩阵的撤销
 * @param mg 指向待撤销的矩阵的指针
 * @return void
*/
void Destroy(MGraph *mg)
{
    for(int i = 0; i<mg->n; i++)
    {
        free(mg->a[i]);
    }
    free(mg->a);
}
/**
 * @brief 边插入 
 * @param mg 指向待邻接矩阵的指针
 * @param u 要插入边的始点
 * @param v 要插入边的终点
 * @param w 边的权值
 * @return 0,插入失败或边已存在;1,边插入成功
*/
int Insert(MGraph *mg, int u, int v, int w)
{
    if(u<0||v<0||u>mg->n-1||v>mg->n-1||u==v)
    {
        return 0;   // 插入失败
    }
    if(mg->a[u][v] != INFINITY)
    {
        printf("边已存在\n");
        return 0;
    }
    mg->a[u][v] = w;    // 插入新边
    mg->a[v][u] = w;
    mg->e++;
    return 1;   // 插入成功
}
/**
 * @brief 查找顶点的树根
 * @param parent 存放所有结点树根的数组
 * @param a 待查找树根的结点
 * @return 返回a的树根
*/
int FindParent(int *parent,int a)
{
    int t = a;
    while(t != parent[t])
    {
        t = parent[t];
    }
    return t;
}
/**
 * @brief 交换函数
 * @param edg 存放边以及边相关信息的数组
 * @param i 数组下标
 * @param j 数组下标
 * @return void
*/
void Swap(Edge *edg,int i, int j)
{
    Edge tmp;
    tmp = edg[i];
    edg[i] = edg[j];
    edg[j] = tmp;
}
/**
 * @brief 分划函数,由快速排序调用
 * @param edg 存放边以及边相关信息的数组
 * @param left 数组下标,左边界
 * @param right 数组下标,右边界
 * @return j,快速排序分划元素的下标
*/
int Partition(Edge *edg,int left,int right)
{
    int i = left;
    int j = right+1;
    do
    {
        do
        {
            i++;
        }while(edg[i].w<edg[left].w);
        do
        {
            j--;
        } while(edg[j].w>edg[left].w);
        if(i<j)
        {
            Swap(edg,i,j);
        }
    }while(i<j);
    Swap(edg, left, j);
    return j;
}
/**
 * @brief 快速排序
 * @param edg 存放边以及边相关信息的数组
 * @param left 数组下标,左边界
 * @param right 数组下标,右边界
 * @return void
*/
void QuickSort(Edge *edg, int left, int right)
{
    if(left<right)
    {
        int j = Partition(edg, left, right);
        QuickSort(edg, left, j-1);
        QuickSort(edg, j+1,right);
    }
}
/**
 * @brief 克鲁斯卡尔算法生成最小代价生成树
 * @param mg 指向邻接矩阵的指针
 * @param edg 存放边以及边相关信息的数组
 * @param sum 计算最小代价生成树权值之和
 * @return void
*/
void Kruskal(MGraph *mg, Edge *edg,int *sum)
{
    int u1,v1;  // 记录顶点的根结点
    int *parent = (int*)malloc(mg->n*sizeof(int));  // 记录所有顶点的根结点,用于判断选取的边是否使生成的树构成回路
    for(int i = 0;i<mg->n;i++)  // 初始化使每个顶点的根结点为自己
    {
        parent[i] = i;
    }
    QuickSort(edg, 0, mg->e-1); // 将保存边的数组按权值由小到大排序
    int count = 0;
    for(int i = 0;i<mg->e;i++)
    {
        u1 = FindParent(parent,edg[i].u);   // 查找顶点的根结点
        v1 = FindParent(parent,edg[i].v);
        if(u1 != v1)    // 根结点不同,表示不属于同一颗树
        {
            count++;
            parent[u1] = v1;    // 合并根节点
            printf("(%d,%d,%d) ",edg[i].u,edg[i].v,edg[i].w); // 输出属于最小生成树的边集
            *sum += edg[i].w;
        }
        if(count == mg->n-1)    // 最小生成树边数e等于顶点数n-1,提前退出
        {
            break;
        }
    }
}

测试数据及其结果分析

普利姆算法生成最小代价生成树图例1
在这里插入图片描述
在这里插入图片描述

图例2
在这里插入图片描述
在这里插入图片描述

程序运行结果:
在这里插入图片描述

克鲁斯卡尔算法生成最小代价生成树图例1
在这里插入图片描述
在这里插入图片描述

图例2
在这里插入图片描述
在这里插入图片描述

程序运行结果:
在这里插入图片描述

总结

由普利姆算法的时间复杂度O(n2),普利姆算法更适合当图中边的数量远大于点的数量的情形,在电路设计中可以应用于印刷电路板的布线问题以减少导线长度。由克鲁斯卡尔算法的时间复杂度O(eloge),克鲁斯卡尔算法更适合当图中点的数量远大于边的数量的情形,与普利姆算法类似,也可以用于优化连接路径的问题。

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

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

相关文章

Lambda架构

1.Lambda架构对大数据处理系统的理解 Lambda架构由Storm的作者Nathan Marz提出&#xff0c;其设计目的在于提供一个能满足大数据系统关键特性的架构&#xff0c;包括高容错、低延迟、可扩展等。其整合离线计算与实时计算&#xff0c;融合不可变性、读写分离和复杂性隔离等原则&…

揭秘“消费即收益”的循环购模式 商家智慧还是消费陷阱?

大家好&#xff0c;我是你们的电商策略顾问吴军。今天&#xff0c;我将带大家深入剖析一种新兴的商业模式——循环购模式&#xff0c;它以其独特的“消费赠礼、每日返利、提现自由”特性&#xff0c;在电商界掀起了不小的波澜。那么&#xff0c;这种模式究竟有何魅力&#xff1…

ip地址突然变了一个城市怎么办

在数字化日益深入的今天&#xff0c;IP地址不仅是网络连接的标识&#xff0c;更是我们网络行为的“身份证”。然而&#xff0c;当您突然发现您的IP地址从一个城市跳转到另一个城市时&#xff0c;这可能会引发一系列的疑问和担忧。本文将带您深入了解IP地址突变的可能原因&#…

Android ViewPostImeInputStage输入事件处理

InputDispatcher向InputChannel使用socket写入输入事件&#xff0c;触发InputEventReceiver调用来接收输入事件。 ViewPostImeInputStage处理view控件的事件 frameworks/base/core/java/android/view/InputEventReceiver.java dispatchInputEvent frameworks/base/core/jav…

Shell编程类-网站检测

Shell编程类-网站检测 面试题参考答法 a(1 2 3 4) echo ${a[0]} echo ${a[*]}这里声明一个数值&#xff0c;并选择逐个调用输出还是全部输出 curl -w %{http_code} urL/IPADDR常用-w选项去判断网站的状态&#xff0c;因为不加选择访问到的网站可能出现乱码无法判断是否网站down…

Nuxt框架中内置组件详解及使用指南(一)

title: Nuxt框架中内置组件详解及使用指南&#xff08;一&#xff09; date: 2024/7/6 updated: 2024/7/6 author: cmdragon excerpt: 本文详细介绍了Nuxt框架中的两个内置组件和的使用方法与功能。确保包裹的内容仅在客户端渲染&#xff0c;适用于处理浏览器特定功能或异步…

第1章 项目背景(学成在线),项目介绍,环境搭建

1.项目背景 1.1 在线教育市场环境 以下内容摘自https://report.iresearch.cn/content/2021/01/358854.shtml 在线教育行业是一个有着极强的广度和深度的行业&#xff0c;从校内到校外&#xff1b;从早幼教到职业培训&#xff1b;从教育工具到全信息化平台等等。 2020年的新…

智慧文旅(景区)解决方案PPT(42页)

智慧文旅解决方案摘要 行业分析中国旅游业正经历消费大众化、需求品质化、发展全域化和产业现代化的发展趋势。《“十三五”旅游业发展规划》的发布&#xff0c;以及文化和旅游部的设立&#xff0c;标志着旅游业的信息化和智能化建设成为国家战略。2018年推出的旅游行业安全防范…

Linux:Ubuntu18.04下开机自启动QT图形化界面

Linux&#xff1a;Ubuntu18.04下开机自启动QT图形化界面 Chapter1 Linux&#xff1a;Ubuntu18.04下开机自启动QT图形化界面一、创建rc.local文件二、建立rc-local.service文件三、启动服务查看启动状态四、重启 Chapter2 将QT应用作为开机自启动&#xff08;Linux系统&#xff…

ePTFE膜(膨体聚四氟乙烯膜)应用前景广阔 本土企业技术水平不断提升

ePTFE膜&#xff08;膨体聚四氟乙烯膜&#xff09;应用前景广阔 本土企业技术水平不断提升 ePTFE膜全称为膨体聚四氟乙烯膜&#xff0c;指以膨体聚四氟乙烯&#xff08;ePTFE&#xff09;为原材料制成的薄膜。ePTFE膜具有耐化学腐蚀、防水透气性好、耐候性佳、耐磨、抗撕裂等优…

跟着峰哥学java 第四天 商品分类 前后端显示

1.后端 1.1mybatis-plus分页查询配置 在商品热卖数据中&#xff0c;只让其显示八条数据 将要使用分页 也就是service.page方法 此时需要配置 mp拦截器 Configuration public class MybatisPlusConfig {Beanpublic PaginationInterceptor paginationInterceptor() {return …

基于Echarts进行图表组件的封装

什么是Echarts 是一个使用js实现的开源可视库&#xff0c;提供了多种图表&#xff0c;但是当我们在项目中进行使用的时候可能就是需要进行一系列的相关配置如&#xff1a; 标题&#xff0c;类型&#xff0c;x轴&#xff0c;y轴等&#xff0c;当我们使用较为频繁的时候就容易导…

[数据结构] 基于交换的排序 冒泡排序快速排序

标题&#xff1a;[数据结构] 基于交换的排序 冒泡排序&&快速排序 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 &#xff08;一&#xff09;冒泡排序 优化后实现&#xff1a; &#xff08;二&#xff09;快速排序 I、实现方法&#xff1a; &#…

Adobe Photoshop 2024 v25.5.1 中文激活版下载以及安装方法教程

软件介绍 Adobe Photoshop 2024 v25.5.1 是Adobe公司的最新版图像处理软件&#xff0c;它提供了强大的图像编辑工具和智能自动化功能&#xff0c;包括图像修复、色彩校正和滤镜效果&#xff0c;以满足专业人士和业余爱好者的需求。这款软件还支持矢量图形设计和实时协作&#…

【程序人生】来CSDN五周年了,简单总结下初心、收获、未来憧憬

最近CSDN站内私信说&#xff0c;已经创作五周年了。想想确实应该写一点东西&#xff0c;总结一下初心是什么、经历了什么、收获了什么、现状怎么样、未来会如何规划写文章这件事。算是我自己的一份总结&#xff0c;也许也可以给一些刚上大学的年轻朋友参考一下&#xff0c;坚持…

【Linux】进程创建和终止 | slab分配器

进程创建 fork 1.fork 之后发生了什么 将给子进程分配新的内存块和内核数据结构&#xff08;形成了新的页表映射&#xff09;将父进程部分数据结构内容拷贝至子进程添加子进程到系统进程列表当中fork 返回&#xff0c;开始调度器调度 这样就可以回答之前返回两个值&#xff1f…

Autosar Dcm配置-0x85服务配置及使用-基于ETAS软件

文章目录 前言Dcm配置DcmDsdDcmDsp代码实现总结前言 0x85服务用来控制DTC设置的开启和关闭。某OEM3.0架构强制支持0x85服务,本文介绍ETAS工具中的配置 Dcm配置 DcmDsd 配置0x85服务 此处配置只在扩展会话下支持(具体需要根据需求决定),两个子服务Disable为0x02,Enable…

3.pwn 函数调用流程,调用约定

前置准备 pop: Pop指令的作用是弹栈&#xff0c;将栈顶的数据弹出到寄存器&#xff0c;然后栈顶指针向下移动一个单位。 具体来说:如pop rax&#xff0c;作用就是mov rax[rsp];add rsp 8; push: Push指令的作用就是压栈&#xff0c;将栈顶指针向上移动一个单位的距离&#xf…

38 IO流

目录 C语言的输入和输出流是什么CIO流stringstream的简单介绍 1. C语言的输入与输出 C语言中我们用到的最频繁的输出方式是scanf和printf&#xff0c;scanf&#xff1a;从标准输入设备&#xff08;键盘&#xff09;读取数据&#xff0c;并将值存在变量中。printf&#xff1a;…

【MySQL】锁(黑马课程)

【MySQL】锁 0. 锁的考察点1. 概述1. 锁的分类1.1 属性分类1.2 粒度分类 2. 全局锁2.1 全局锁操作2.2.1 备份问题 3. 表级锁3.1 表锁3.2 语法3.3 表共享读锁&#xff08;读锁&#xff09;3.4 表独占写锁&#xff08;写锁&#xff09;3.5 元数据锁(meta data lock, MDL)3.6 意向…