Linux嵌入式学习——数据结构——线性表的链式结构

news2024/9/21 10:49:09

线性表的链式存储
    解决顺序存储的缺点,插入和删除,动态存储问题。
特点:
    线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素,存储单元可以是连续的,也可以不连续。可以被存储在任意内存未被占用的位置上。
    
    所以前面的顺序表只需要存储数据元素信息就可以了。在链式结构中还需要一个元素存储下一个元素的地址。
    
    为了表示每个数据元素,ai与其直接后继数据元素ai+1之间的逻辑关系,对ai来说,除了存储其本身的信息外,还需要存一个指示器直接后续的信息。把存储元素信息的域叫数据域,把存储直接后继位置的域叫指针域。这两部分信息组成数据元素ai的存储映像,叫结点(Node);
    
    
    
    单链表中,c语言的描述
 

typedef struct person {
    char name[32];
    char sex;
    int age;
    int score;
}DATATYPE;

typedef struct node {
    DATATYPE data;
    struct node *next,*prev;         ->这里必须要加指针
}LinkNode;

typedef struct list {
    LinkNode *head;
    int tlen;  链表元素总个数 这里可以不要
    int clen; 当前节点个数

     (mutex 这部分有可能是并发的,有可能需要加上互斥锁)
}LinkList;

       以下是一个链表(LinkList)的操作集,这些操作包括创建链表、在链表头部或尾部插入节点、显示链表内容、查找链表中的节点、删除链表中的节点、修改链表节点的数据和销毁链表。这里,我假设LinkList是一个指向链表头节点的指针,而LinkNode是链表节点的结构体。DATATYPE是一个占位符,表示链表节点存储的数据类型,可能是整型、字符型或其他自定义类型。以下是对每个函数的基本实现思路(不包含完整代码):

1. LinkList *CreateLinkList(int len);

这个函数用于创建一个具有指定长度(len)的空链表。如果len是0,则创建一个空链表(只有一个头节点,头节点的下一个指针指向NULL)。如果len大于0,则可能需要根据实际需求创建len个空节点(或带初始值的节点)的链表。但通常,len参数在这里可能不太适用,因为链表长度是动态变化的,更常见的做法是不带参数地创建一个空链表。

2. int InsertHeadLinkList(LinkList *list, DATATYPE data);

在链表的头部插入一个新节点,该节点包含指定的数据data。函数返回成功或失败的状态码。

若需要在当前表头结点插入一个新结点 , 只需要修改一个next指针(新结点的next指针) , 可以分两步完成

  1. 定义节点结构体:首先,需要定义一个节点结构体,该结构体包含数据域和指针域。数据域用于存储节点的数据,指针域用于存储指向下一个节点的指针。

  2. 初始化链表:创建一个头节点,该节点不存储有效数据,仅作为链表的入口点。头节点的指针域初始化为NULL,表示链表为空。

  3. 插入新节点:对于每次插入操作,首先创建一个新节点,并为其数据域赋值。然后,将新节点的指针域指向头节点的下一个节点(即原链表的第一个节点),最后修改头节点的指针域,使其指向新节点。

* 修改新结点的next指针 , 使其指向当前的表头结点

* 更改表头指针的值 , 使其指向新节点(之前的表头指针指向的是15 , 修改后指向为新数据的)

3. int ShowLinkList(LinkList *list);

遍历链表,并打印出每个节点的数据。这个函数没有返回值(或返回一个状态码表示成功或失败,但通常打印操作不需要)。

4. LinkNode *FindLinkList(LinkList *list, char *name);

在链表中查找具有指定名称(name)的节点。由于函数返回类型为LinkNode *,它返回找到的节点的指针,如果未找到则返回NULL。这里假设每个节点都有一个name属性,这与DATATYPE数据类型的定义可能不一致,需要根据实际数据结构调整。

其中可以定义一个函数指针,来找结构体中不同得东西来达到解耦合的情况

5. int DeleteLinkList(LinkList *list, char *name);

从链表中删除具有指定名称(name)的节点。函数返回成功或失败的状态码。

删除中间节点

(1)使tmp指向想要删除的节点

(2)将tmp的下一个的prev与tmp本身的上一个相等 

(3)使tmp的上一个的nxt与tmp本身的next相等 得到指向tmp节点的下一个节点。 

(4)但如果是最后的节点 ,只要使前一个的下一个置为空就行。但无法找到本身的下一个,会发生错误,所以需要加上一个判断。

6. int ReviseLinkList(LinkList *list, char *name, DATATYPE data);

使链表反转

定义三个指针,分别表示正在操作的节点(prve),以及该节点的上一个(tmp),该节点的下一个(next)

(1)将tmp=head;prve=NULL;next=tmp->next;

  (2)反转tmp本身的prve和next指针

tmp-》next=prve;  tmp->prev=next;

(3)将最初定义的大指针一个一个往后移,直到最后tmp为空,

prve = tmp;

tmp=next;

next =next->next;

(4)将head与最初定义的prev相等。

7. int DestroyLinkList(LinkList *list);

销毁链表,释放链表占用的所有内存空间。函数返回成功或失败的状态码。

使tmp指向第一个节点,一个一个往后释放,最后释放head;

8. int InsertTailLinkList(LinkList *list, DATATYPE data);

在链表的尾部插入一个新节点,该节点包含指定的数据data。函数返回成功或失败的状态码。

9. int ModifyDouLinkList(LinkList *list, DATATYPE data);

改变某个节点的数据。函数返回成功或失败的状态码

9. int InserPosDouLinList(LinkList *list, DATATYPE data);

(1)如果head里没有节点   clen=0(链表是空)或者想要插入的pos=0;直接调用头插

(2)head后有节点

将tmp移到想要插入位置的前一个节点;

while(pos-1)

{

       tmp=tmp->next;

使newnode的prev指向tmp,newnode 的next指向原来tmp指向的位置;

newnode->prve=tmp;

newnode->next=tmp->next;

 如果tmp后面没东西,直接使tmp的next等于newnode

(原本就定义了newnode的prev和next是NULL);

如果tmp后面 ,使原本ttmp的下一个的前向指针指向newnode 同时使 tmp 的后向指针指向newnode。

即:

if(tmp->next)

{

tmp->next->prev=newnode

}

tmp->next=newnode;

pos和clen相同就是尾插。

.h

#ifndef DOULINK_H
#define DOULINK_H
typedef struct{
    char name[32];
    char sex;
    int age;
    int score;
}DATATYPE;
typedef int (*PFUN)(DATATYPE*data,void* arg);
typedef struct node {
    DATATYPE data;
    struct node *next,*prev;
}DouLinkNode;

typedef struct{
    DouLinkNode *head;
    int clen;
}DouLinkList;
typedef enum{DIR_FORWARD,DIR_BACKWARD}DIRECT;
DouLinkList* CreateDouLinkList();
int InsertHeadLinkList(DouLinkList *list, DATATYPE *data);
int ShowDouLinkList(DouLinkList *list,DIRECT direct);
int GetSizeDouLinkList(DouLinkList *list);
DouLinkNode *FindLinkList(DouLinkList *list, PFUN fun,void* arg);
int RevertDouLinkList(DouLinkList *list);
int DeleteLinkList(DouLinkList *list, PFUN fun,void* arg);
int IsEmptyDouLinkList(DouLinkList *list);
int ModifyDouLinkList(DouLinkList *list,PFUN fun,void* arg,DATATYPE *data);
int DestroyDouLinkList(DouLinkList **list);
int InserPosDouLinkList(DouLinkList *list,DATATYPE *data,int pos);
#endif // DOULINK_H

.c

#include "doulink.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

DouLinkList *CreateDouLinkList()
{
    //DouLinkList dl ;
    DouLinkList* dl = (DouLinkList*)malloc(sizeof(DouLinkList));
    if(NULL == dl)
    {
        perror("CreateDouLinkList malloc");
        //exit(1);
        return NULL;
    }
    dl->head =NULL;
    dl->clen = 0 ;

    return dl;
}

int InsertHeadLinkList(DouLinkList *list, DATATYPE *data)
{
    DouLinkNode*newnode = malloc(sizeof(DouLinkNode));
    if(NULL == newnode)
    {
        perror("InsertHeadLinkList malloc");
        return 1;
    }
    memcpy(&newnode->data,data,sizeof(DATATYPE));
    newnode->next = NULL;
    newnode->prev= NULL;

    if(0==list->clen)//empty
    {
        list->head = newnode;
    }
    else
    {
        newnode->next = list->head;
        list->head->prev = newnode;
        list->head = newnode;
    }
    list->clen++;
    return 0;


}

int ShowDouLinkList(DouLinkList *list, DIRECT direct)
{
    int i = 0 ;
    DouLinkNode* tmp = list->head;
    if(direct==DIR_FORWARD)
    {
        for(i=0;i<GetSizeDouLinkList(list);i++)
        {
            printf("%s %c %d %d\n",tmp->data.name,tmp->data.sex,tmp->data.age,tmp->data.score);
            tmp=tmp->next;
        }
    }
    else
    {
        while(tmp->next)
        {
            tmp=tmp->next;
        }
        for(i=0;i<GetSizeDouLinkList(list);i++)
        {
            printf("%s %c %d %d\n",tmp->data.name,tmp->data.sex,tmp->data.age,tmp->data.score);
            tmp=tmp->prev;
        }
    }
    return 0;
}

int GetSizeDouLinkList(DouLinkList *list)
{
    return list->clen;
}

DouLinkNode *FindLinkList(DouLinkList *list, PFUN fun, void *arg)
{
    DouLinkNode* tmp = list->head;
    int size = GetSizeDouLinkList(list);
    int i =  0;
    for(i = 0 ;i<size;i++)
    {
        //if(0==strcmp(tmp->data.name))
        if(fun(&tmp->data,arg))
        {
            return tmp;
        }
        tmp= tmp->next;
    }
    return NULL;
}

int RevertDouLinkList(DouLinkList *list)
{
    int size = GetSizeDouLinkList(list);
    if(size<2)
    {
        return 0;
    }

    DouLinkNode* prev= NULL;
    DouLinkNode* tmp = list->head;
    DouLinkNode*next= tmp->next;
    while(1)
    {
        tmp->next = prev;
        tmp->prev = next;
        prev= tmp;
        tmp = next;
        if(NULL == tmp)
        {
            break;
        }
        next =next->next;
    }
    list->head = prev;
    return 0;
}

int DeleteLinkList(DouLinkList *list, PFUN fun, void *arg)
{
    if(NULL == list)
    {
        fprintf(stderr,"DouLinkList is null");
        return 1;
    }
    if(IsEmptyDouLinkList(list))
    {
        fprintf(stderr,"DouLinkList is empty");
        return 1;
    }
    DouLinkNode* ret = FindLinkList(list,fun,arg);
    if(NULL==ret)
    {
        fprintf(stderr,"DeleteLinkList error,cant find\n");
        return 1;
    }
    if(ret == list->head)
    {
        list->head = ret->next;
        list->head->prev = NULL;
    }
    else
    {
        if(ret->next)
        ret->next->prev = ret->prev;
        ret->prev->next = ret->next;
    }

    free(ret);
    list->clen--;
    return 0;
}

int IsEmptyDouLinkList(DouLinkList *list)
{
    return 0 == list->clen;
}

int ModifyDouLinkList(DouLinkList *list, PFUN fun, void *arg, DATATYPE *data)
{
    DouLinkNode* ret = FindLinkList(list,fun,arg);
    if(NULL == ret)
    {
        fprintf(stderr,"ModifyDouLinkList error,cant find\n");
        return 1;
    }
    memcpy(&ret->data,data,sizeof(DATATYPE));
    return 0;
}

int DestroyDouLinkList(DouLinkList **list)
{
    DouLinkNode* tmp=(*list)->head;
    while(tmp)
    {
    (*list)->head=(*list)->head->next;
    free(tmp);
    tmp = (*list)->head;

    }
    free(*list);
    (*list)= NULL;
    return 0;
}

int InserPosDouLinkList(DouLinkList *list, DATATYPE *data,int pos)
{
    if(pos<0 ||pos>GetSizeDouLinkList(list))
    {
        fprintf(stderr,"InserPosDouLinkList error,index error\n");
        return 1;

    }
    if(IsEmptyDouLinkList(list) || 0 == pos)
    {
        return InsertHeadLinkList(list,data);
    }
    else
    {
        DouLinkNode* tmp = list->head;
        tmp= list->head;
        DouLinkNode* newnode = (DouLinkNode*)malloc(sizeof(DouLinkNode));
        if(NULL == newnode)
        {
            perror("InserPosDouLinkList malloc");
            return 1;
        }
        memcpy(&newnode->data,data,sizeof(DATATYPE));
        newnode->prev = NULL;
        newnode->next = NULL;
        int i = pos-1;
        while(i--)
        {
            tmp=tmp->next;
        }
        newnode ->prev = tmp;
        newnode->next = tmp->next;

        if(tmp->next)
        {
        tmp->next->prev = newnode;
        }
        tmp->next = newnode;
    }
    list->clen++;
    return 0;
}

man.c

#include <stdio.h>
#include "doulink.h"
#include <string.h>
int findbyname(DATATYPE*data,void* arg)
{
    return (0 == strcmp(data->name,(char*)arg));
}
int findbyage(DATATYPE*data,void* arg)
{
    return data->age == *(int*)arg;
}
int main()
{
    DATATYPE data[5]={
        {"zhangsan",'m',20,70},
        {"lisi",'f',21,60},
        {"wangmazi",'m',25,80},
        {"liubei",'f',30,85},
        {"caocao",'f',40,90},
    };

    DouLinkList* dl = CreateDouLinkList();

    InsertHeadLinkList(dl,&data[0]);
    InsertHeadLinkList(dl,&data[1]);
    InsertHeadLinkList(dl,&data[2]);

    ShowDouLinkList(dl,DIR_FORWARD);
    printf("-------------back---------------\n");
    ShowDouLinkList(dl,DIR_BACKWARD);
    printf("-------------find---------------\n");
    //    char want_name[]="lisi";
    //    //DouLinkNode* tmp = FindLinkList(dl,findbyname,want_name);
    //    int want_age = 25;
    //    DouLinkNode* tmp = FindLinkList(dl,findbyage,&want_age);
    //    if(NULL == tmp)
    //    {
    //        printf("can't find person ,name:%s\n",want_name);
    //    }
    //    else
    //    {

    //        printf("%s:%d\n",tmp->data.name,tmp->data.score);
    //    }

    //    RevertDouLinkList(dl);
    //    printf("-------------rev---------------\n");
    //    ShowDouLinkList(dl,DIR_FORWARD);
    //    DeleteLinkList(dl,findbyname,"lisi");
    //    printf("-------------del forware---------------\n");
    //    ShowDouLinkList(dl,DIR_FORWARD);
    //    printf("-------------back---------------\n");
    //    ShowDouLinkList(dl,DIR_BACKWARD);

    //    ModifyDouLinkList(dl,findbyname,"zhangsan",&data[3]);
    //    printf("-------------modify---------------\n");
    //    ShowDouLinkList(dl,DIR_FORWARD);

    InserPosDouLinkList(dl,&data[3],3);
    printf("-------------pos---------------\n");
    ShowDouLinkList(dl,DIR_FORWARD);
    printf("-------------back---------------\n");
    ShowDouLinkList(dl,DIR_BACKWARD);


    DestroyDouLinkList(&dl);

    printf("Hello World!\n");
    return 0;
}

顺序表和链表 优缺点
   
      

顺序表(Array)

优点

  1. 随机访问:顺序表支持通过索引快速访问任意位置的元素,时间复杂度为O(1)。
  2. 存储密度高:顺序表在物理存储上是连续的,存储密度大(即存储空间利用率高,因为不需要额外存储指针等信息)。
  3. 缓存友好:顺序表的数据在物理上连续存储,因此可能更好地利用CPU缓存,提高访问效率。

缺点

  1. 插入和删除操作成本高:在顺序表的中间或开始位置插入或删除元素时,需要移动大量的元素来保持数据的连续性,时间复杂度为O(n)。
  2. 扩容问题:当顺序表的容量不足以存储更多元素时,需要进行扩容操作,这涉及到申请新的内存空间、复制原有数据到新空间等步骤,可能会比较耗时。
  3. 空间利用率可能不高:在顺序表中,如果预留的空间过大,但实际存储的元素较少,会导致空间浪费;如果预留的空间过小,又需要频繁扩容,影响效率。

链表(LinkedList)

优点

  1. 插入和删除操作灵活:链表在插入和删除元素时,只需要改变指针的指向,不需要移动大量的元素,时间复杂度为O(1)(在已知位置进行操作时)。这使得链表非常适合于频繁插入和删除操作的场景。
  2. 动态分配内存:链表中的节点可以动态地申请和释放内存,使得链表的大小可以根据需要动态变化,无需担心空间浪费或扩容问题。

缺点

  1. 访问元素效率低:访问链表中的元素需要从头节点开始遍历,直到找到所需的元素,时间复杂度为O(n)。
  2. 空间利用率相对较低:链表中每个节点除了存储数据本身外,还需要额外存储指针(或引用)信息,这增加了存储空间的开销。
  3. 缓存不友好:由于链表的节点在物理上不一定连续存储,因此可能无法有效地利用CPU缓存,导致访问效率下降。

     
            
    循环链表     

 简单的来说,就是将原来单链表中最有一个元素的next指针指向第一个元素或头结点,链表就成了一个环,头尾相连,就成了循环链表。circultlar linker list.
        
        注意非空表,和空表。多数会加入头结点。

        原来结束的条件是        

p->next != NULL ------->>>>> p-next != Head   
或者写成 指定长度clen;        

 双向链表
    double link list
 
    typedef struct DulNode
    {
    
        ElemType date;
        struct DulNode *pri;
        sturct DulNode *next;
    }DulNode,*DuLinkList;
    

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

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

相关文章

vue3如何实现分面漏斗图

如下图&#xff1a; 这里我选择采用Vue3g2plot进行实现。 参考网址&#xff1a;https://g2plot.antv.antgroup.com/zh/examples/more-plots/funnel/#facet-transpose 核心代码如下&#xff1a; const data [{ stage: 简历筛选, number: 253, company: A公司 },{ stage: 初…

东京裸机云多IP服务器全面分析

东京裸机云多IP服务器是一种提供多IP地址分配和高性能网络服务的云计算解决方案&#xff0c;广泛应用于需要多IP管理和高稳定性的网络应用。下面将从几个方面具体介绍东京裸机云多IP服务器&#xff0c;rak部落为您整理发布东京裸机云多IP服务器的全面分析。 在数字化时代&#…

Docker 部署常用中间件(redis,rabbitMQ,mysql8,es,kibana,nginx等)亲测成功~~~

Docker 部署常用中间件 在日常开发中必要的环境&#xff0c;大多数都是单点后续持续更新集群模式~~~ docker 安装reids docker pull redis:7.2.5 编辑redis.conf # 绑定地址&#xff0c;默认只允许本机访问 # bind 192.168.1.100 10.0.0.1 # bind 127.0.0.1 ::1 bind 0.0…

学员心得 | 做好这几点,轻松通关云计算HCIE3.0!

烈日当空&#xff0c;骄阳似火&#xff0c;在这炎炎夏日迎来了自己今年一份满意的答卷--华为HCIE云计算“通过”的e妹儿&#xff08;嘿嘿&#xff01;证书也快了&#xff09;。此时此刻&#xff0c;想把自己“辛勤劳作”备考IE的一些经历和建议留给后来的同路人。 “世界上最可…

MySQL练手 --- 619. 只出现一次的最大数字

题目链接&#xff1a;619. 只出现一次的最大数字 思路 这是一个简单题&#xff0c;只出现一次的最大数字&#xff0c;顾名思义&#xff0c;分两个阶段&#xff0c;第一个阶段筛选出只出现一次的数字&#xff0c;第二阶段在生成的新表中筛选出最大值即可。 解题过程 生成一张…

iOS object-C 解答算法:找到所有数组中消失的数字(leetCode-448)

找到所有数组中消失的数字(leetCode-448) 题目如下图:(也可以到leetCode上看完整题目,题号448) 光看题看可能有点难以理解,我们结合示例1来理解一下这道题. 有8个整数的数组 nums [4,3,2,7,8,2,3,1], 求在闭区间[1,8]范围内(即1,2,3,4,5,6,7,8)的数字,哪几个没有出现在数组 …

华为OD机试 - 二叉树计算(Java JS Python C C++)

题目描述 给出一个二叉树如下图所示: 请由该二叉树生成一个新的二叉树,它满足其树中的每个节点将包含原始树中的左子树和右子树的和。 左子树表示该节点左侧叶子节点为根节点的一颗新树;右子树表示该节点右侧叶子节点为根节点的一颗新树。 输入描述 2行整数,第1行表示二叉…

MySQL数据库-备份恢复

一、MySQL日志管理 1.为什么需要日志 用于排错用来做数据分析了解程序的运行情况&#xff0c;了解MySQL的性能 2.日志作用 在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这样情况下&#xff0c;就必须保证数据的安全性和完整性&#…

Spring AI (五) Message 消息

5.Message 消息 在Spring AI提供的接口中&#xff0c;每条信息的角色总共分为三类&#xff1a; SystemMessage&#xff1a;系统限制信息&#xff0c;这种信息在对话中的权重很大&#xff0c;AI会优先依据SystemMessage里的内容进行回复&#xff1b; UserMessage&#xff1a;用…

c++笔记4

目录 深度优先搜索DFS DFS的复杂度 DFS与递归 递归与暴力枚举 递归树 DFS与栈 DFS的搜索剪枝 搜索剪枝与优化 可行性剪枝 最优化剪枝 减少等效的分支 优化搜索顺序 搜索的记忆化 搜索的复杂度 大多时候&#xff0c;搜索的复杂度都是指数级的。各种剪枝方案&#…

RK3568笔记四十三:MPU6050驱动开发(硬件I2C_3)

若该文为原创文章&#xff0c;转载请注明原文出处。 正点原子提供的I2C有测试ap3216c&#xff0c;SH3001等传感器&#xff0c;根据手册操作可以实现效果。 这里记录使用I2C3驱动MPU6050. 记录原因是前面有模拟I2C&#xff0c;但硬件如何使用&#xff0c;有点不是很清楚&#…

[C++] string管理:深浅拷贝写时拷贝

文章目录 拷贝问题的引入问题代码string类的构造函数String 类的析构函数测试入口函数&#xff08;问题&#xff09;详细分析 浅拷贝深拷贝传统版与现代版的String类传统String类现代版String类 写时拷贝先构造的对象后析构的影响写时拷贝举例及测试样例代码举例测试用例 拷贝问…

BGP之选路MED

原理概述 当一台BGP路由器中存在多条去往同一目标网络的BGP路由时&#xff0c;BGP协议会对这些BGP路由的属性进行比较&#xff0c;以确定去往该目标网络的最优BGP路由。BGP路由属性的比较顺序为Preferred Value属性、Local Preference属性、路由生成方式、AS_Path属性、Origin属…

react中路由跳转以及路由传参

一、路由跳转 1.安装插件 npm install react-router-dom 2.路由配置 路由配置&#xff1a;react中简单的配置路由-CSDN博客 3.实现代码 // src/page/index/index.js// 引入 import { Link, useNavigate } from "react-router-dom";function IndexPage() {const …

大数据之Oracle同步Doris数据不一致问题

数据同步架构如下&#xff1a; 出现的问题&#xff1a; doris中的数据条数 源库中的数据条数 总数完全不一致。 出现问题的原因&#xff1a; 在Dinky中建立表结构时&#xff0c;缺少对主键属性的限制 primary key(ID) not enforced 加上如上语句&#xff0c;数据条数解决一致 …

WPF+Mvvm项目入门完整教程-仓储管理系统(二)

目录 一、搭建一个主界面框架二、实现步骤1.主界面区域划分2.主界面区域实现 一、搭建一个主界面框架 主要实现主界面的框架样式和基础功能。这里特别说明一下&#xff0c;由于MvvmLight 已经过时不在维护&#xff0c;本项目决定将MvvmLight框架变更为 CommunityToolkit.Mvvm …

标题:探索pdf2image:将PDF文档转化为图像的Python魔法

标题&#xff1a;探索pdf2image&#xff1a;将PDF文档转化为图 像的Python魔法 背景 在数字时代&#xff0c;我们经常需要处理各种格式的文档&#xff0c;尤其是PDF文件。PDF以其跨平台的可读性和稳定性而广受欢迎。然而&#xff0c;有时我们需要将PDF文件转换成图像格式&am…

Golang | Leetcode Golang题解之第282题给表达式添加运算符

题目&#xff1a; 题解&#xff1a; func addOperators(num string, target int) (ans []string) {n : len(num)var backtrack func(expr []byte, i, res, mul int)backtrack func(expr []byte, i, res, mul int) {if i n {if res target {ans append(ans, string(expr))}…

Linux--Socket编程预备

目录 1. 理解源 IP 地址和目的 IP 地址 2.端口号 2.1端口号(port)是传输层协议的内容 2.2端口号范围划分 2.3理解 "端口号" 和 "进程 ID" 2.4理解 socket 3.传输层的典型代表 3.1认识 TCP 协议 3.2认识 UDP 协议 4. 网络字节序 5. socket 编程接…

【数据结构】--- 栈和队列

前言 前面学习了数据结构的顺序表、单链表、双向循环链表这些结构&#xff1b;现在就来学习栈和队列&#xff0c;这里可以简单的说栈和队列是具有特殊化的线性表 一、栈 1.1、栈的概念和结构 栈是一种遵循先入后出逻辑的线性数据结构。 栈是一种特殊的线性表&#xff0c;它只允…