链表的数据域怎么使用结构体_一步一步教你从零开始写C语言链表

为什么要学习链表?

链表主要有以下几大特性:

1、解决数组无法存储多种数据类型的问题。

2、解决数组中,元素个数无法改变的限制(C99的变长数组,C++也有变长数组可以实现)。

3、数组移动元素的过程中,要对元素进行大范围的移动,很耗时间,效率也不高。

先来感性的认识一下链表,我们先来认识下简单的链表:

5ac3f33d12d4e4ade09190d3cfb8b089.png

从这幅图我们得出以下信息:

这个简单链表的构成:

头指针(Header),若干个节点(节点包括了数据域和指针域),最后一个节点要指向空。

实现原理:头指针指向链表的第一个节点,然后第一个节点中的指针指向下一个节点,然后依次指到最后一个节点,这样就构成了一条链表。

接下来看看链表的数据结构:

struct  list_node

{

int data ; //数据域,用于存储数据

struct list_node *next ; //指针,可以用来访问节点数据,也可以遍历,指向下一个节点

};

那么如何来创建一个链表的一个节点呢?

我们写个程序演示一下:

#include

#include

#include

struct  list_node

{

int data ; 

struct list_node *next ;

};

typedef struct list_node list_single ;

int main(void)

{

list_single *node = NULL ;          //1、首先,当然是定义一个头指针

node = (list_single *)malloc(sizeof(list_single)); //2、然后分配内存空间

if(node == NULL){

printf("malloc fair!\n");

}

memset(node,0,sizeof(list_single)); //3、清一下

node->data = 100 ;     //4、给链表节点的数据赋值

node->next = NULL ;                 //5、将链表的指针域指向空

printf("%d\n",node->data);

free(node);

return 0 ;

}

    那么,这仅仅只是创建一个链表中的一个节点,为了好看,我们把创建节点封装成函数,以后想创建多少个节点,我们就可以反复调用一个函数来创建,会很方便:

list_single *create_list_node(int data)

{

list_single *node = NULL ;

node = (list_single *)malloc(sizeof(list_single));

if(node == NULL){

printf("malloc fair!\n");

}

memset(node,0,sizeof(list_single));

node->data = 100 ;

node->next = NULL ;

return node ;

}

接下来在程序上完成的程序:

#include

#include

#include

struct  list_node

{

int data ; 

struct list_node *next ;

};

typedef struct list_node list_single ;

list_single *create_list_node(int data)

{

list_single *node = NULL ;

node = (list_single *)malloc(sizeof(list_single));

if(node == NULL){

printf("malloc fair!\n");

}

memset(node,0,sizeof(list_single));

node->data = data;

node->next = NULL ;

return node ;

}

int main(void)

{

int data = 100 ;

list_single *node_ptr = create_list_node(data); //创建一个节点

printf("node_ptr->data=%d\n",node_ptr->data);   //打印节点里的数据

printf("node_ptr->next=%d\n",node_ptr->next);  

free(node_ptr);

return 0 ;

}

执行结果 :

1d7da770a3dd71b34b95bd71cfa3b966.png

这样我们就完成一个链表节点的创建了,那么它现在的样子如下图:

链表的结构里,数据存储了100,因为这个链表只有一个节点,所以它的指针域指向了NULL。

7d0e05040727a95cf9b8eab9284b55b5.png

上面只是建立一个单链表的基本雏形,接下来咱们再来增加一点难度。如果创建多个单链表节点,实现单链表的增删改查?把单链表应用起来。

1、首先定义一个单链表的数据结构

184b910a0c4503eff65e4a364f248d18.png

创建节点函数原型可定义如下:

struct list *create_node(int data) ;

如何创建单链表的节点,主要分以下步骤:

(1)给当前的每个节点的数据结构配置定量的空间大小

   ep : struct list *node = malloc(sizeof(struct list));

(2)清节点数据(由于结构体变量在未初始化的时候,数据是脏的)

   ep : memset(node,0,sizeof(struct list));

(3)给节点初始化数据

   ep : node->id = data ; 

(4)将该节点的指针域设置为NULL

   ep : node->next = NULL ;

2、单链表的尾插:

尾插节点函数原型可定义如下:

7e7a574ef3d666c80ea615df3a9f4437.png

如何将当前链表和新的节点相连接?只要实现:

header->next = new 

尾插流程如下:

(1)获取当前节点的位置,也就是访问头节点

   ep : struct list *p = header ;

(2)判断是否为最后一个节点,如果不是,移动到下一个节点,如果是,将数据插入尾部。

   ep : while(NULL != p->next) p = p->next ;

        p->next = new ;

3、单链表的头插

7907abbe46d8362d7ec4d497b82278b7.png

很好理解,头插就是把新的节点插在原来的节点和原来节点的下一个节点之间的一个节点。如图,新的节点插在头节点和节点1。

所以可以推出头插流程如下:

(1)获取当前节点的位置,也就是访问头节点

    ep : struct list *p = header ;

(2)新的节点的下一个节点设置为原来头节点的下一个节点(第一个节点)

    ep : new->next = p->next ;

(3)原来的头节点的下一个节点设置为现在新插入的头节点

    ep : p->next = new ;

4、单链表的遍历

6a7f1d01b4442dc5743d669a6758eccb.png

如图为一条单向链表的模型,看图知道该链表由头节点和若干个节点组成,最后一个节点(尾节点)为NULL 。

从图中可以得出信息,如果我们要打印出各个节点的数据,要考虑以下问题:

(1)需要打印头节点吗?(头节点肯定是不用打印的,因为这是我们为了操作方便而设置的一个节点)。

(2)这条链表有多少个节点我们怎么知道?(通过判断该链表是否已经到达了尾节点,标志就是NULL)

那么可以得到流程如下:

(1)获取当前节点的位置,也就是访问头节点

    ep : struct list *p = header ;

(2)由于头节点我们不需要去打印它,这时候,初始化打印的节点需要从第一个节点开始。

    ep : p = p->next ;  

(3)判断是否为最后一个节点,如果不是,先打印第一个节点的数据(1),然后移动到下一个节点(2),重复这两个步骤。

   如果是最后一个节点,直接打印数据即可。

    while(NULL != p->next){ printf("node:%d\n",p->data) ; p = p->next ;}

    printf("node:%d\n",p->data);

    当然还可以一句代码解决,这样就达到了先偏移,后取数据。

    while(NULL != p->next){ p = p->next ; printf("node:%d\n",p->data) ; }

5、单链表的删除

删除节点的函数原型可定义如下:

int detele_list_node(struct list *pH , int data);

单向链表的删除要考虑两种情况,一种的普通节点的删除(当然,头节点不能算)

还有一种是尾节点的前一个节点的删除情况,注意,删除完节点还需要释放对应节点的内存空间。

cb86f05f16bf1de0c05ec03d0025cc70.png

4b5d22c51703b9e344ffb1f19aa6e2c7.png

删除节点的设计流程:

(1)先定义两个指针,一个表示当前的节点,另一个表示当前节点的上一个节点。

    ep : struct list *p = header ;  //当前节点

         struct list *prev = NULL ; //当前节点的上一个节点

(2)遍历整个链表,同时保存当前节点的前一个节点

    ep : while(NULL != p->next)

        { 

          //保存了当前的节点的前一个节点

          prev = p ;  

          //保存当前偏移的节点

          p = p->next ; 

          return 0 ;

        }

(3)在遍历的过程中查找要删除的数据

    ep : while(NULL != p->next)

        { 

          //保存了当前的节点的前一个节点

          prev = p ;  

          //保存当前偏移的节点

          p = p->next ; 

          //查找到了数据

          if(p->id == data)

          {

          }

          return 0 ;

        }

(4)查找到了数据后,分两种情况删除

    ep : 普通节点的删除

        if(p->id == data)

        {

            prev->next = p->next ;

            free(p);

        }

    ep : 考虑尾节点的下一个节点为NULL的节点删除

        if(p->id == data)

        {

            if(p->next == NULL)

            {

                prev->next = NULL ;

                free(p);

            }

        }

6、单链表的逆序

5aa31e4a5d8b2dcaf2de1e9e935c81f4.png

逆序步骤:

ead6438310a264a39cf3bfa1b3154090.png

   单链表的基本操作流程咱们基本搞懂了,下面写一个程序,这将会变得非常非常的简单。

#include

#include

#include

typedef struct slist

{

int id ;

struct slist *next ;

}L;

//创建一个节点 

L *create_node(int data)

{

//给每个节点分配结构体一样的空间大小 

L *p = malloc(sizeof(L));

if(NULL == p)

{

printf("malloc error!\n");

return NULL ;

}

//由于结构体在未初始化的时候一样是脏数据,所以要清 

memset(p,0,sizeof(L));

//初始化第一个节点 

p->id = data ; 

//将节点的后继指针设置为NULL 

p->next = NULL ;

}

//链表的尾插 

void tail_insert(L *pH , L *new)

{

//获取当前的位置 

L *p = pH ; 

//如果当前位置的下一个节点不为空 

while(NULL != p->next)

{

//移动到下一个节点 

p = p->next ;

}

//如果跳出以上循环,所以已经到了NULL的这个位置

//此时直接把新插入的节点赋值给NULL这个位置 

p->next = new ;

}

//链表的头插 

void top_insert(L *pH , L *new)

{

L *p = pH ;

new->next = p->next ;

p->next = new ;

}

//链表的遍历 

void Print_node(L *pH)

{

//获取当前的位置 

L *p = pH ;

//获取第一个节点的位置 

p = p->next ;

//如果当前位置的下一个节点不为空 

while(NULL != p->next)

{

//(1)打印节点的数据 

printf("id:%d\n",p->id);

//(2)移动到下一个节点,如果条件仍为真,则重复(1),再(2) 

p = p->next ;

}

//如果当前位置的下一个节点为空,则打印数据

//说明只有一个节点 

printf("id:%d\n",p->id);

}

//删除链表中的节点 

int detele_list_node(L * pH , int data)

{

//获取当前头节点的位置 

L *p = pH ;

L *prev = NULL;

while(NULL != p->next)

{

//保存当前节点的前一个节点的指针 

prev = p ;

//然后让当前的指针继续往后移动 

p = p->next ;

//判断,找到了要删除的数据  

if(p->id == data)

{

//两种情况,一种是普通节点,还有一种是尾节点

if(p->next != NULL)  //普通节点的情况 

{

prev->next = p->next ;

free(p);

}

else //尾节点的情况 

{

prev->next = NULL ; //将这个尾节点的上一个节点的指针域指向空 

free(p); 

}

return 0  ;

}

}

printf("没有要删除的节点\n");

return -1 ;

}

void trave_list(L * pH)

{

//保存第一个节点的位置 

L *p = pH->next;

L *pBack;

int i = 0 ;

if(p->next == NULL || p == NULL)

return ;

while(NULL != p->next) //遍历链表 

{

//保存第一个节点的下一个节点 

pBack = p->next ; 

//找到第一个有效节点,其实就是头指针的下一个节点 

if(p == pH->next) 

{

//第一个有效节点就是最后一个节点,所以要指向NULL 

p->next = NULL ; 

else

{

/*

new->next = p->next ;

p->next = new ;

*/

p->next = pH->next ; //尾部连接 

}

pH->next = p ; //头部连接 

p = pBack ; //走下一个节点 

}

top_insert(pH,p); //插入最后一个节点 

}

int main(int argc , char **argv) 

{

//创建第一个节点 

int i ;

L *header = create_node(0); 

for(i = 1 ; i < 10 ; i++)

{

tail_insert(header,create_node(i));

}

Print_node(header);

detele_list_node(header,5);

putchar('\n');

Print_node(header);

putchar('\n');

trave_list(header);

Print_node(header);

return 0 ;

}

运行结果:

167cba8e20049c143ff29a0f2b028a2f.png

当然,单链表存在一定的弊端,就是查找数据和删除数据的时候比较麻烦,而双链表的出现就是为了解决它的弊端:

双链表的引入是为了解决单链表的不足:

(1)双链表可以往前遍历,也可以往后遍历,具有两个方向

双链表的节点 = 有效数据 + 两个指针(分别指向前一个节点和后一个节点)

双向链表的图形结构描述:

20c8c77142a36d5f8c2981f27316aca0.png

struct double_list                                   struct double_list

{                                                            {

    数据域;                  ep :------->                   int data ;

    指针域(前向指针) ;                                   struct double_list *prev ;

    指针域(后向指针) ;                                   struct double_list *next ;

};                                                             };

1、双向链表的创建

struct list *create_node(int data) ;

创建步骤(与单链表类似,就是多了一个指针):

(1)给节点申请空间:

   ep : struct double_list *p = malloc(sizeof(struct double_list));

(2)初始化数据域

   ep : p->data = data ;

(3)初始化指针域

   ep : p->prev = NULL ; 

        p->next = NULL ;

2、双向链表的尾插

双向链表尾插节点的函数可以定义如下:

void double_list_tail_insert(struct double_list *header , struct double_list *new) ;

尾插图示操作:

尾插的步骤:

(1)找到链表的尾节点

   ep : 和单链表差不多

        DL *p = header ;

        while(NULL != p->next)

            p = p->next ;

(2)将新的节点连接到尾节点的后面成为新的节点

   1.原来的next指针指向新节点的首地址。            p->next = new ;

   2.新节点的prev指针指向原来的尾节点的首地址。 new->prev = p;

3、双向链表的头插

双向链表头插节点的函数可以定义如下:

void double_list_top_insert(struct double_list *header , struct double_list *new) ;

9bc460066586ebd4ef275406c349d442.png

3b36fd1f19a28d9a06e9553a7f9e4b8a.png

4、双向链表的遍历

4.1 正向遍历

    void double_list_for_each(DL *header)

    步骤:和单链表完全一致,没什么好写的。

4.2 反向遍历

    void double_list_for_each_nx(DL *header)

    步骤:(1)和单链表一样,先循环找到最后一个节点的地址

          (2)再依靠prev指针循环往前移动

             2.1 先打印最后一个数据  ep : printf("%d ",p->data);

             2.2 向前循环遍历         ep : p = p->prev ;

             判断条件:header->prev != p->prev,

             header保存的是头节点的地址,

             header->prev保存的是头节点的prev的地址,

             header->next保存的是头节点的next的地址,

             头节点在创建的时候:

             header->prev = NULL ;

             header->next = NULL ;

             所以这个条件这样写header->prev = NULL也是对的。

5、双向链表节点的删除

假设需要删除节点1:    

    首先:

    (1)获取当前节点的地址: 

        ep : p = header;

    (2)遍历所有的节点,找到要删除的节点:

        ep : 

        while(NULL != p->next)

        {

            p = p->next ;

            if(p->data == data)

            {

            }

        }

    (3)找到要删除的数据以后,需要做判断,判断两种情况,这和单链表差不多

    3.1 如果找到当前节点的下一个节点不为空

    3.1.1    

        那就把当前节点的prev节点指向要删除的这个节点的prev

        因为当前的prev节点保存的是要删除的上一个节点的指针 

        p->next->prev = p->prev ;

    3.1.2    

        然后将当前节点的prev指针(也就是上一个节点的指针)指向当前节点(要删除的)的下一个节点:

        p->prev->next = p->next ;

    3.1.3    

        最后释放删除指针的空间:

        free(p);

    3.2 如果找到当前节点的下一个节点为空

    3.2.1   

    直接把当前指针(要删除的节点)的prev指针(保存着当前指针的上一个节点的地址)的下一个next指针设置为空。

    p->prev->next = NULL ;

    3.2.2

    将删除的指针的空间释放:

    free(p);

    看来,双链表学起来比单链表容易多了!确实啊,多了一个方向,操作起来就更加容易了,但是多了一个方向,一维多了一个指针,相比增加了一定的复杂度,但是,只要牢记prev指针和next指针的指向,那么,手画一图,代码即可写出!

下面给一个案例实现一下双向链表:

#include

#include

#include

//创建一个双链表的数据结构

typedef struct __double_list

{

int data ;

struct __double_list *prev ;

struct __double_list *next ;

}DL ; 

//创建双向链表并插入一个节点 

DL *create_dl_node(int data)

{

DL *p = malloc(sizeof(DL));

if(NULL == p)

{

printf("create dl node fair!\n");

return NULL ;

}

//初始化数据 

p->data = data ;

//初始化指针 

p->next = NULL ;

p->prev = NULL ;

}

//双向链表的尾插 

void double_list_tail_insert(DL *header , DL *new)

{

//取得当前节点的地址 

DL *p = header ;

//找到链表的尾节点 

while(NULL != p->next)

{

//找不到接着找 

p = p->next ;

}

//找到了尾节点,指向新节点的首地址 

p->next = new ;

//新节点的prev指针指向原来的尾节点的首地址。

new->prev = p;

}

//双向链表的头插(也就是插在两个节点之间的插入方式)

void double_list_top_insert(DL *header , DL *new)

{

//新节点的next指针指向原来的节点一的地址

new->next = header->next ; 

//判断当前节点的下一个节点的地址是否为空

if(NULL != header->next) 

header->next->prev = new ; //节点1的prev指针指向新节点的地址 

header->next = new ;

new->prev = header ;

}

//双向链表的正向遍历 

void double_list_for_each(DL *header)

{

DL *p = header ;

while(NULL != p->next)

{

p = p->next ;

printf("%d ",p->data);

}

}

//双向链表的反向遍历 

void double_list_for_each_nx(DL *header)

{

DL *p = header ;

//先找到尾节点

while(NULL != p->next)

{

p = p->next ;

//已经找到了尾节点,向前遍历,注意,遍历到头节点之前

//限制条件: != 头结点 

while(NULL != p->prev)

{

printf("%d ",p->data);

p = p->prev ;

}

}

//双向链表节点的删除

int double_list_delete_node(DL *header , int data)

{

//取得当前节点 

DL *p = header;

//遍历所有的节点 

while(NULL != p->next)

{

p = p->next ;

//找到了对应要删除的数据了 

if(p->data == data)

{

//一样存在两种情况

//(1)当前节点的下一个节点不为空

if(p->next != NULL)

{

//那就把当前节点的prev节点指向要删除的这个节点的prev

//因为当前的prev节点保存的是要删除的上一个节点的指针 

p->next->prev = p->prev ;

//还要指定它的next节点 

p->prev->next = p->next ;

free(p);

}

//(2)当前节点的下一个节点为空 

else

{

//把 

p->prev->next = NULL ;

free(p); 

}

return 0 ;

}

}

printf("\n没有找到对应要删除的节点,或者节点已经被删除!\n");

return -1 ;

int main(void)

{

int i ;

DL *header = create_dl_node(0);

for(i = 0 ; i < 10 ; i++)

{

//双向链表的尾插 

//double_list_tail_insert(header,create_dl_node(i));

//双向链表的头插 

double_list_top_insert(header,create_dl_node(i));

}

//双向链表的正向遍历 

printf("双向链表的正向遍历:");

double_list_delete_node(header,5);

double_list_for_each(header);

// double_list_delete_node(header,9);

double_list_delete_node(header,5);

putchar('\n');

printf("双向链表的反向遍历:");

double_list_for_each_nx(header);

return 0 ;

}

运行结果:

0ae95ffcb495854a8288516f4cdb6bb9.png

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

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

相关文章

python计算bmi_Python编程语言:如何用Python编程来判断体重指数BMI是否健康

上一篇小编分享了自己学习Python语言有关字符串和模块time使用的相关知识&#xff0c;这一篇小编分享给大家的是比较有趣的运用&#xff0c;那就是如何用Python编程来表示自己体重BMI。 用Python程序来算出我们自己的BMI指数&#xff0c;来判断我们自己的健康情况&#xff0c;首…

drbd实现mysql地热备_heartheartbeat+drbd+mysql主库热备

1 环境主机名网卡磁盘mastereth0 桥接模式 eth0(192.168.1.10) 自定义模式(VMnet2)(192.168.2.10)VIP 192.168.1.200/210系统盘20G外接磁盘slaveeth0 桥接模式(192.168.1.20) eth1 自定义模式(VMnet2)(192.168.2.20)VIP 192.168.1.200/210系统盘20G外接磁盘server3eth0 桥接模式…

dba的前景_运维、测试、程序员,这些技术岗位哪个更有前景?

在一个初具规模的互联网公司&#xff0c;从业务方面出发&#xff0c;有很多岗位类型&#xff0c;比如运营、客服、市场、产品、设计、技术等等。在这些大类下面&#xff0c;还要细分各种小类&#xff0c;以技术为例&#xff0c;可分为前端(客户端)、后端、测试、运维、DBA等等&…

免安装版的mysql步骤_mysql免安装版的安装方法及步骤

mysql免安装版的安装方法及步骤发布时间&#xff1a;2020-07-15 14:07:18来源&#xff1a;亿速云阅读&#xff1a;83作者&#xff1a;清晨小编给大家分享一下mysql免安装版的安装方法及步骤&#xff0c;希望大家阅读完这篇文章后大所收获&#xff0c;下面让我们一起去探讨吧&am…

java spring框架 注解_spring框架之注解的使用

原标题&#xff1a;spring框架之注解的使用今天是刘小爱自学Java的第122天。感谢你的观看&#xff0c;谢谢你。学习内容安排如下&#xff1a;Spring注解的使用。JavaWeb项目的搭建。Spring的Web集成。本来还计划学Spring的junit测试集成的&#xff0c;结果又没时间了。一、Spri…

idea 代码格式化插件_IDEA非常棒的插件,阿里巴巴约定成文的代码公约规范

无规矩&#xff0c;不方圆。每个人都有自己的编码风格&#xff0c;每个公司也有自己的代码规范。规范的代码&#xff0c;无论是自己日常维护&#xff0c;还是以后接盘者来接盘&#xff0c;都能快速定位上手&#xff0c;大大提高效率。作为一个IDEA万年爱好者&#xff0c;这些最…

switch最大选项数目_随时随地学习C语言之3—if和switch哪个效率高?

之前学习C语言的时候&#xff0c;我经常有一个疑问&#xff0c;既然有if-else if-else结构的多分支选择语句&#xff0c;C语言为何还要制定switch这种多分支选择语句呢&#xff1f;直到两年前在分析ARM平台C语言反汇编代码的时候&#xff0c;才终于明白了switch-case这种结构存…

onclick 获取img 里面的id_红魔5S游戏手机如何解锁bl获取第三方面具root权限

自从苹果阵营发布S系列&#xff0c;安卓系列也学习苹果的步伐&#xff0c;开始发布S系列&#xff0c;这不努比亚也发不了今年夏天的游戏手机&#xff0c;红魔5S游戏手机。红魔5S相对于红魔5其实从配置来看&#xff0c;三大件配置变化并不大&#xff0c;主要还是细节上的一些提升…

java 工厂模式的写法_设计模式-工厂模式

一、概述什么是工厂模式&#xff1f;工厂模式(Factory Pattern)是最常见的一种设计模式之一。它主要是提供一种创建对象的最佳方法&#xff01;为什么要学习工厂模式&#xff1f;与通过new来创建对象不同&#xff0c;使用工厂模式创建对象不会对客户端暴露创建逻辑&#xff0c;…

jackson 序列化_jackson序列化与反序列化的应用实践

作者 | zhouweixin 来源 | urlify.cn/iEbiAz66套java从入门到精通实战课程分享1 相关概念序列化: 把对象转换为字节序列的过程称为对象的序列化反序列化: 把字节序列恢复为对象的过程称为对象的反序列化2 序列化的作用用于把内存中的对象状态保存到一个文件中或者数据库中用于网…

java mqtt客户端_基于 t-io 实现一个 mqtt5 协议之 mica-mqtt

一、简介 MQTT 全称为 Message Queuing Telemetry Transport&#xff08;消息队列遥测传输&#xff09;是一种基于发布/订阅范式的“轻量级”消息协议&#xff0c;由 IBM 发布。目前使用比较广泛的就是 mqtt 3.1.1&#xff08;2014年制定&#xff09;&#xff0c;mqtt 5.0&…

java自我介绍_JAVA面试技巧之自我介绍

【如何进行自我介绍】自我介绍这个问题&#xff0c;不用多说了&#xff0c;面试必定会问&#xff01;如果想要在自我介绍的时候就能够打动面试官&#xff0c;吸引面试官对我们的兴趣&#xff0c;那么像我们这种接受过Java培训的程序员的自我介绍当然不能和应届生或者其他非技术…

java excel 操作 poi_Java使用apache poi进行excel相关操作

一.基本介绍1.1、Apache POI介绍Apache POI是一个可以进行微软的文档进行开源库&#xff0c;可以操作的文档类型包括word、ppt、excel、visio、outlook....本文主要针对Apache POI对excel的操作进行介绍&#xff0c;主要包括如何创建一个excel、录入数据、读取excel数据的方式。…

程序解析excel中的图片_产品日志丨支持导入Excel中的图片amp;批量修改后期实体字段...

本次安捷秀又迎来了一个大版本更新&#xff0c;除了大家呼声很高的「实体模块导入 Excel 」外&#xff0c;还有「批量编辑实体」&#xff0c;「支持右击修改」以及针对海外用户的「全页面支持英文」等功能的新增与优化&#xff0c;一起来看看吧。导入功能优化AGILESHOT&#xf…

java不同垃圾回收器_细述 Java垃圾回收机制→Types of Java Garbage Collectors

本文非原创&#xff0c;翻译自Types of Java Garbage Collectors在Java中为对象分配和释放内存空间都是由垃圾回收线程自动执行完成的。和C语言不一样的是Java程序员不需要手动写垃圾回收相关的代码。这是使得Java如此流行&#xff0c;同时也是Java能帮助程序员写出更好的Java应…

js修改地址栏url_不同寻常的地址栏过渡

前几天&#xff0c;我在推特上看到这样一张图。原来地址栏还能这么玩&#xff0c;瞬间就觉得自己弱爆了。然后我决定去实现一下这个效果&#xff0c;然后做成一个库。画了一个晚上&#xff0c;终于做好了。这是最后的成果。这个库使用非常的简单。你只需要&#xff0c;yarn add…

kaggle数据集_ArXiv170万篇论文数据集上线Kaggle!

大数据文摘出品学术圈的朋友对ArXiv肯定都不陌生。在将近30年的时间里&#xff0c;ArXiv通过公开访问学术文章为公众和研究社区提供了一个更高效的学术成果沟通平台&#xff0c;从物理学到计算机科学的许多子学科&#xff0c;以及介于两者之间的所有内容&#xff0c;包括数学&a…

万能驱动xp离线版_教你用SC封装软件来封装XP系统

今天我们来讲解一下如何用SC软件来封装XP系统。今天的讲解只演示基本的SC封装软件&#xff0c;具体的封装前的准备工具&#xff0c;我们不进行讲解&#xff0c;当然前期的准备工作也是有很多&#xff0c;首先我们先要安装虚拟机软件&#xff0c;并在虚拟机上面安装好原版的XP系…

java dispo lock_java实现文件上传和下载(1)

原理&#xff1a; 使用html 的 标签&#xff0c;提交form 的几个属性必须为&#xff1a; methodpost encTypemultipart/form-data;组件:smartUpload或者commons fileuploadsmartUpload代码实现1。文件预览function showImage(obj){var strobj.value;$("#id").html(&q…

剪板机自动上下料_机器人联轴器,用于机器人自动化上下料

关注点击蓝字&#xff0c;关注我吧纤薄型机器人联轴器&#xff0c;可搬运重量范围从 1kg 至 1,000kg&#xff0c;用于高效机器人自动化机床上下料&#xff0c;多年来受到广泛认可。紧凑型微型联轴器适合于最近迅速发展的小型机器人应用领域。无人操作时工艺可靠性高模块的特殊混…