数据结构:线性表(详解)

线性表

线性表的知识框架:

线性表的定义:

线性表是具有相同数据类型的n(n > 0)个数据元素的有限序列,当n = 0时线性表为一个空表。
若用L命名为线性表,则
数据集合为L= {a1,a2,…,an},其中a1称为表头元素,an称为表尾元素。除第一个元素外,每个元素有且仅有一个直接前驱。除最后一个元素外,每个元素有且仅有一个直接后继。

线性表的特点:

1.表中元素的个数有限
2.表中元素具有逻辑上的顺序表,表中元素有其先后次序
3.表中元素都是数据元素,每个元素都是单个元素
4.表中元素的数据类型相同,所以每个元素占有相同大小的存储空间
5.线性表是一种逻辑结构,表示元素之间一对一的相邻关系。顺序表和链表是指存储结构

线性表的顺序存储结构

顺序表的的定义:

线性表的顺序存储又称顺序表。它是用一组地址连续的存储单元依次存储线性表中的数据元素。

顺序表的特点:

1.顺序表的存储密度高,每个结点只存储数据结构。
2.顺序表逻辑上相邻的元素在物理上也相邻,所以插入和删除操作需要移动大量数据
3.顺序表是随机访问,即通过首地址和元素序号可在时间O(1)内找到指定的元素

顺序表代码实现:

    顺序表储存结构:

typedef int SLTDataType;
typedef struct SeqList
{SLDatatype capacity;//数组的容量SLDatatype size;//有效数据SLDatatype* arr;
}SL;
顺序表所有接口:
//初始化/销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);//顺序表的头部/尾部插入
void SLPushBack(SL* ps, SLDatatype x);
void SLPushFront(SL* ps, SLDatatype x);//顺序表的头部/尾部删除
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);//指定位置之前插入数据/删除指定位置数据
void SLInsert(SL* ps, SLDatatype pos, SLDatatype x);
void SLErase(SL* ps, SLDatatype pos);
顺序表初始化和销毁:
void SLInit(SL* ps)
{ps->arr = NULL;ps->capacity = ps->size = 0;
}

顺序表输出:

void SLPrint(SL* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}printf("\n");
}
顺序表扩容:
void SLIncreaseCapacity(SL* ps)
{if (ps->capacity == ps->size)//当有效数据与实际数据相等则需要扩容{SLDatatype NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;SLDatatype* tmp = (SLDatatype*)realloc(ps->arr, NewCapacity * sizeof(int));if (tmp == NULL){perror("malloc error");exit(-1);}ps->arr = tmp; //仅当 SL 结构被销毁时,用于 ps->arr 的内存才会被释放。ps->capacity = NewCapacity;}
}
顺序表尾插:
void SLPushBack(SL* ps, SLDatatype x)
{assert(ps);
SLIncreaseCapacity(ps);//判断是否需要扩容
ps->arr[ps->size++] = x;
}

顺序表尾插输出:

顺序表头插:
void SLPushFront(SL* ps, SLDatatype x)
{assert(ps);
SLIncreaseCapacity(ps);//判断是否需要扩容
for (int i = ps->size; i > 0; i--)
{ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}

顺序表头插输出:

顺序表尾删:
void SLPopBack(SL* ps)
{assert(ps);assert(ps->size);//判断数组是否为空ps->size--;//size--只是在逻辑层面删除了而已,但物理层面还是实际存在的 //只是我们不将它看成 顺序表的一部分,所以就可以理解为删除了         
}

顺序表尾删输出:

顺序表头删:
void SLPopFront(SL* ps)
{assert(ps);assert(ps->size);for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

顺序表头删输出:

在指定位置插入:
void SLInsert(SL* ps, SLDatatype pos, SLDatatype x)
{assert(ps);SLCheckCapacity(ps);assert(pos >= 0 && pos < ps->size);//pos如果小于0那就没什么意义并且不能等于ps->size如果等于那就相当于尾插了for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;ps->size++;
}

指定位置输出:

在指定位置删除:
void SLErase(SL* ps, SLDatatype pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

指定位置输出:

顺序表的优缺点:

  优点:1.空间利用率高

             2.插入删除方便

             3.结构简单

  缺点:1.访问效率低

            2.插入和删除操作可能导致元素移动

            3.难以实现部分元素的随机访问

线性表的链式存储结构:

单链表的定义:

单链表(SLL)是一种由节点序列组成的链接数据结构,每个节点包含一个数据元素和一个指向下一个节点的指针。单链表以其简单性和效率而闻名,广泛应用于各种应用程序。                                                                                  单链表结构如下:

单链表代码实现:

单链表存储结构:
typedef int SLTDataType;
typedef struct SList
{struct SList* next;//指针域SLTDataType data;//数据域
}SL;
单链表所有接口:

//链表的输出、销毁
void SLPrint(SL* phead);
void SListDesTroy(SL** pphead);//链表的尾插、头插
void SLPushBack(SL** pphead, SLTDataType x);
void SLPushFront(SL** pphead, SLTDataType x);//链表的尾删、头删
void SLPopBack(SL** pphead);
void SLPopFront(SL** pphead);//查找
SL* SLTFind(SL* phead, SLTDataType x);//在指定位置之前插入数据、之后插入数据
void SLTInsert(SL** pphead, SL* pos, SLTDataType x);
void SLTInsertAfter(SL* pos, SLTDataType x);//删除pos节点、pos之后的节点
void SLTErase(SL** pphead, SL* pos);
void SLTEraseAfter(SL* pos);
单链表输出:

void SLPrint(SL* phead)
{SL* pur = phead;while (pur){printf("%d->", pur->data);pur = pur->next;}printf("NULL");printf("\n");
}
单链表销毁:

void SListDesTroy(SL** pphead)
{void SListDesTroy(SL** pphead)
{assert(pphead && *pphead);SL* Pfast = (*pphead)->next;SL* Pslow = *pphead;while (Pslow && Pfast != NULL){free(Pslow);Pslow = NULL;Pslow = Pfast;Pfast = Pfast->next;}//销毁尾节点free(Pslow);Pslow = NULL;//free完之后再将头节点置为空*pphead = NULL;
}
}

销毁链表输出:

扩容单链表空间:

SL* SLTBuyNode(SLTDataType x)
{SL* Newnode = (SL*)malloc(sizeof(SL));SL*	ptmp = Newnode;if (ptmp == NULL){perror("malloc error");exit(-1);}Newnode->data = x;Newnode->next = NULL;return Newnode;
}
单链表尾插:

//为什么需要二级指针而不用一级指针呢,因为传一级的话改变不了它的指向而二级指针可以
//就相当于C语言里面传值和传地址一个道理
void SLPushBack(SL** pphead, SLTDataType x)
{//pphead:相当于指针指向节点的那个指针的地址//*pphead:相当于节点地址//**pphead:相当于节点里的数据assert(pphead);//判断pphead指针是否为空和节点是否为空//assert(*pphead);//判断pphead节点是否为空SL* newnode = SLTBuyNode(x);if (*pphead == NULL)//当链表为空时那么newnode就可以作为链表的新节点{*pphead = newnode;return;}//链表不为空SL* Ptmp = *pphead;while (Ptmp->next){Ptmp = Ptmp->next;}Ptmp->next = newnode;
}

单链表尾插输出:

本质上,单指针用于“只读”操作,而双指针用于“读写”操作。

以下是一个简化的类比来说明区别:

想象一个链表就像一列火车车厢。每个车厢代表列表中的一个节点,车厢之间的连接代表指针。

单指针就像火车上的乘客。他们可以从一辆车厢移动到另一辆车厢,观察每辆车厢的内容(数据访问),但他们不能直接拆卸或连接车厢(修改列表结构)。

双指针就像火车乘务员。他们不仅有权在车厢之间移动,还可以解耦和耦合车厢,修改列车的组成(修改列表结构)。
如果还是搞不懂可以点开这个链接,这个博主写的非常好http://t.csdnimg.cn/yNoDo

单链表头插:

void SLPushFront(SL** pphead, SLTDataType x)
{assert(pphead);SL* newnode = SLTBuyNode(x);if (*pphead == NULL){*pphead = newnode;return;}newnode->next = *pphead;*pphead = newnode;
}

单链表头插输出:

单链表尾插:


void SLPopBack(SL** pphead)
{assert(pphead && *pphead);//判断pphead指针是否为空和节点是否为空//只有一个节点//因为->符号的优先级比*符号的优先级高所以不加括号就是pphead->nextif ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}//多个节点SL* Pfast = *pphead;SL* Pslow = NULL;//如果是用Pfast去判断的话那等while结束的时候到它会指向NULL,Plast会指向最后一个节点//最后我们是把NULL赋给Pfast->next以此来达到尾删的效果,那这样都不是删除最后一个节点而是给NULL赋NULL。//所以是要用Pfast->next去进行判断while (Pfast->next){Pslow = Pfast;Pfast = Pfast->next;}Pslow->next = NULL;//最后别忘记销毁尾节点free(Pfast);Pfast = NULL;
}

单链表尾删输出:

单链表头删:

void SLPopFront(SL** pphead)
{assert(pphead && *pphead);//只有一个节点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}//多个节点SL* next = (*pphead)->next;free(*pphead);*pphead = next;
}

单链表头删输出:

单链表查找:

SL* SLTFind(SL* phead, SLTDataType x)
{assert(phead);SL* Pcur = phead;while (Pcur){if (Pcur->data == x){return Pcur;}Pcur = Pcur->next;}return NULL;
}

查找输出:

在指定位置之前插入数据:
void SLTInsert(SL** pphead, SL* pos, SLTDataType x)
{assert(pphead && *pphead);assert(pos);//判断pos指针是否为空SL* newnode = SLTBuyNode(x);//扩容一个新节点//假设pos为头节点if (pos == *pphead){   //头插SLPushFront(pphead , x);return;//插入完新数据直接return}SL* Pur = *pphead;//不为头节点while (Pur){if (Pur->next == pos){Pur->next = newnode;newnode->next = pos;}Pur = Pur->next;}return;
}

指定位置之前输出:

在指定位置之后插入数据:

//尾插不需要pos之前的指针,所以pphead在这里不起作用
void SLTInsertAfter(SL* pos, SLTDataType x)
{assert(pos);SL* newnode = SLTBuyNode(x);SL* Ptur = pos->next;//防止找不到它的下一个节点pos->next = newnode;newnode->next = Ptur;
}

指定位置之后输出:

删除pos指针:
void SLTErase(SL** pphead, SL* pos)
{assert(pphead && *pphead);assert(pos);//pos刚好是头结点,执行头删if (*pphead == pos) {//头删SLPopFront(pphead);return;}SL* Pur = *pphead;//不为头节点while (Pur->next != pos){Pur = Pur->next;}Pur->next = pos->next;free(pos);pos = NULL;return;//为什么错误?上面为什么对?//因为上面就是他先把pos置为空但是那个循环不会结束等到Pur->NULL时//那这又与pos相等了,如果再去访问pos->next就会报错//访问空指针的next就会出问题但是访问有效指针的next且那个next为空这样并不会报错/*while (Pur){if (Pur->next == pos){Pur->next = pos->next;free(pos);pos = NULL;}Pur = Pur->next;}*/
}

删除pos指针输出:

删除pos之后节点
void SLTEraseAfter(SL* pos)
{assert(pos);SL* Pur = pos->next;//保存Pur节点等下需要销毁这个节点pos->next = pos->next->next;free(Pur);Pur = NULL;
}

删除pos之后节点输出:

                                     上述代码全部都是用不带头节点编写而成 

单链表的优缺点:

 优点:
             1.简单易懂
             2.插入和删除操作高效
             3.动态内存管理

缺点:
             1.无法随机访问
             2.插入操作会破坏原有顺序
             3.无法向后遍历

双链表的定义:

在计算机科学中,双链表(DLL)是一种由元素序列组成的链接数据结构,每个元素称为节点,每个节点都有两个指针:一个指向序列中的下一个节点,另一个指向前面的节点。每个节点还包含一个数据元素。

双链表代码实现:

双链表存储结构:
typedef int LTDataType;
typedef struct DList
{struct DList* next;//后继节点struct DList* prev;//前驱节点LTDataType data;//节点数据
}DL;
双链表所有接口:
//初始化、销毁、输出
void LTInit(DL** pphead);
void LTDesTroy(DL** pphead);
void LTPrint(DL* phead);//尾插、头插
void LTPushBack(DL* phead, LTDataType x);
void LTPushFront(DL* phead, LTDataType x);//头删、尾删
void LTPopBack(DL* phead);
void LTPopFront(DL* phead);//查找
DL* LTFind(DL* phead, LTDataType x);//在pos位置之后插入数据、删除pos位置的数据
void LTInsert(DL* pos, LTDataType x);
void LTErase(DL* pos);
双链表初始化:
void LTInit(DL** pphead)
{*pphead = (DL*)malloc(sizeof(DL));//为哨兵位开辟空间if (*pphead == NULL){perror("malloc error");exit(-1);}//双链表节点的初始化是前驱和后继指针都是指向自身(*pphead)->next = (*pphead)->prev = *pphead;(*pphead)->data = 0;
}

双链表初始化输出:

双链表销毁:
void  LTDesTroy(DL** pphead)
{assert(pphead);assert(*pphead);DL* Pfast = (*pphead)->next->next;DL* Pslow = (*pphead)->next;while (Pslow != NULL){free(Pslow);Pslow = NULL;Pslow = Pfast;Pfast = Pfast->next;}free(*pphead);*pphead = NULL;
}

双链表销毁输出:

扩展双链表空间:
DL* DLTBuyNode(LTDataType x)
{DL* NewNode = (DL*)malloc(sizeof(DL));//为新节点开辟空间DL* Ptmp = NewNode;if (Ptmp == NULL){perror("malloc error");exit(-1);}Ptmp->data = x;Ptmp->next = Ptmp->prev = NewNode;return NewNode;
}
双链表输出:
void LTPrint(DL* phead)
{assert(phead);DL* Pur = phead->next;//因为哨兵位没有数据所以不需要遍历它while (Pur != phead){printf("%d->", Pur->data);Pur = Pur->next;}printf("\n");
}
双链表尾插:
void LTPushBack(DL* phead, LTDataType x)
{assert(phead);//判断phead指针是否为空DL* Newnode = DLTBuyNode(x);//先改变新节点Newnode->next = phead;//让新节点的后继指针指向头节点(原先是尾节点指向哨兵位的前驱)Newnode->prev = phead->prev;//让新节点的前驱指针指向尾节点//再改变尾节点phead->prev->next = Newnode;//让尾节点的后继指针指向新节点phead->prev = Newnode;//哨兵位的前驱指针指向新节点
}

双链表尾插输出:

双链表头插:
//双链表的头插并不是在哨兵位之前插入数据而是第一个有效节点之前插入
//在哨兵位之前插入数据就相当尾插,因为哨兵位前面就是最后一个有效节点
void LTPushFront(DL* phead, LTDataType x)
{assert(phead);DL* Newnode = DLTBuyNode(x);//先改变新节点的指向Newnode->next = phead->next;Newnode->prev = phead;phead->next->prev = Newnode;phead->next = Newnode;
}

双链表头插输出:

双链表尾删:
void LTPopBack(DL* phead)
{assert(phead);assert(phead->next != phead);//哨兵位指向自己就相当于双链表为空DL* pre = phead->prev;//先让尾节点的前一个节点的next指向哨兵位再让哨兵位的前驱指针指向尾节点的前一个节点那这样下 //来尾节点就相当于被销毁了phead->prev->prev->next = phead;//phead->prev就相当于尾节点然后phead->prev->prev就相当 //于尾节点的前一个节点phead->prev = phead->prev->prev;free(pre);pre = NULL;}

双链表尾删输出:

双链表头删:
void LTPopFront(DL* phead)
{assert(phead);assert(phead->next != phead);DL* next = phead->next;//如果不先保存原哨兵位的next地址那删除完节点后再去 //free(phead>next)这样就是删除新头节点了并不是再删除旧的头节点phead->next->next->prev = phead;phead->next = phead->next->next;free(next);next = NULL;
}

双链表头删输出:

双链表查找:
DL* LTFind(DL* phead, LTDataType x)
{assert(phead);DL* Pur = phead->next;//区别于与单链表的是这里传的并不是头节点而是头节点nextwhile (Pur != phead){if (Pur->data == x){return Pur;}Pur = Pur->next;}return NULL;
}

双链表查找输出:

在指定位置之后插入数据:
void LTInsert(DL* pos, LTDataType x)
{assert(pos);DL* Newnode = DLTBuyNode(x);//依然是先修改新节点的指向Newnode->next = pos->next;Newnode->prev = pos;//不能先改变pos->next的指向因为修改了就找不到pos后面的节点//所以指向先改变pos后面节点的指向然后再改变pos的指向pos->next->prev = Newnode;pos->next = Newnode;}

在指定位置之后插入数据输出:

删除pos位置的数据:
void LTErase(DL* pos)
{assert(pos);//先改变pos前面的节点那就找不到pos后面的节点,所以只能先改变后面再改变前面pos->next->prev = pos->prev;pos->prev->next = pos->next;
}

删除pos位置的数据输出:

链表与顺序表的比较:

1.结构:

链表: 链表由一系列节点组成,每个节点包含数据元素和指向下一个节点的指针。节点之间的连接是通过指针实现的,因此链表是一种非连续的存储结构

顺序表顺序表将元素存储在连续的内存空间中,每个元素占用一个固定大小的单元。元素之间的访问通过索引实现,因此顺序表是一种连续的存储结构

2.逻辑结构与物理结构:

采用顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻。而采用链式存储时,逻辑上相邻的元素,物理存储位置不一定相邻,对应的逻辑关系时通过指针链接来表示的。

3.遍历、插入、删除操作:

插入:在链表中插入新元素,需要修改相邻节点的指针以保持链表的顺序。在顺序表中插入新元素,如果空间不足则需要重新分配内存或移动现有元素。

删除:在链表中删除元素,需要修改相邻节点的指针以跳过被删除的元素。在顺序表中删除元素,需要将被删除元素后面的元素向前移动一个位置。

遍历在链表中遍历元素,需要逐个遍历节点并访问数据元素。在顺序表中遍历元素,可以使用循环访问每个元素

4.应用:

链表: 适用于需要频繁插入和删除操作的情景,因为链表的插入和删除操作效率较高,且不需要预先分配固定数量的内存空间。例如,缓存实现、栈和队列的底层实现等。

顺序表: 适用于需要随机访问数据的情景,因为顺序表的随机访问效率较高,可以直接通过索引访问任意位置的元素。例如,数组的底层实现、符号表等。

总结:

在选择数据结构时,需要根据具体应用场景权衡利弊。如果需要频繁插入和删除操作,并且对随机访问性能要求不高,则链表是一个不错的选择。如果需要随机访问数据,并且插入和删除操作不频繁,则顺序表是一个更好的选择。

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

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

相关文章

从零开始搭建Springboot项目脚手架1:新建项目

1、技术栈 SpringBoot 3.2.5&#xff1a; 2、 新建项目 使用SpringInitializr 选择Lombok、Configuration Processor、Spring Web&#xff0c;同时IDEA也要安装Lombok插件 删除多余的Maven目录、Maven文件&#xff0c;把HELP.md改成README.md。 当然前提是已经安装好Maven和配…

力扣70 爬楼梯 C语言 动态规划 递归

题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 阶 示例 2…

Java多线程:常见的线程的创建方法及Thread类详解

目录 一.并发编程相关概念 线程与进程 多线程 Java中线程的状态 二.线程的创建方法 方法一&#xff1a;继承Thread类 方法二&#xff1a;实现Runnable接口 其他方法 三.Thread类详解 Thread常见构造方法 Thread常见属性 Thread常见方法 start() 与 run() sleep(…

Redis教程——主从复制

在上篇文章我们学习了Redis教程——管道&#xff0c;这篇文章学习Redis教程——主从复制。 主从复制 为了数据更加安全可靠&#xff0c;在实际的项目中&#xff0c;肯定是有多个Redis服务&#xff0c;主机Redis以写为主&#xff0c;从机Redis以读为主&#xff0c;当主机Redis…

长难句打卡5.7

In December 2010 America’s Federal Trade Commission (FTC) proposed adding a “do not track” (DNT) option to Internet browsers, so that users could tell advertisers that they did not want to be followed. 2010年12月&#xff0c;美国美国联邦贸易委员会(FTC)提…

在数字化转型的浪潮中,CBDB百数服务商如何破浪前行?

在信息化时代&#xff0c;传统咨询企业面临着数字化转型的挑战与机遇。如何利用数字化技术提升业务效率、增强客户黏性&#xff0c;成为了行业关注的焦点。云南析比迪彼企业管理有限公司&#xff08;CBDB&#xff09;作为云南地区的企业咨询服务提供商&#xff0c;率先与百数展…

Springai入门

一、概述 1.1发展历史 1.2大模型 大模型&#xff0c;是指具有大规模参数和复杂计算结构的机器学习模型。这些模型通常由深度神经网络构建而成&#xff0c;拥有数十亿甚至数千亿个参数。其设计目的在于提高模型的表达能力和预测性能&#xff0c;以应对更加复杂的任务和数据&…

虚幻引擎5 Gameplay框架(二)

Gameplay重要类及重要功能使用方法&#xff08;一&#xff09; 配置LOG类及PlayerController的网络机制 探索验证GamePlay重要函数、类的执行顺序与含义 我们定义自己的日志&#xff0c;专门建立一个存放自己日志的类&#xff0c;这个类继承自BlueprintFunctionLibrary 然后…

零基础怎么学鸿蒙开发?

对于零基础的学习者来说&#xff0c;掌握鸿蒙开发不仅是迈向新技术的第一步&#xff0c;更是开拓职业道路的重要机遇。随着鸿蒙系统在各行各业的应用逐渐扩展&#xff0c;对于掌握这一项技术的开发人员需求也随之增长。下文将为大家提供针对零基础学习鸿蒙开发的逻辑&#xff0…

提高曝光率与点击率:海外KOL推广与社交媒体算法的完美结合

在当今数字化时代&#xff0c;社交媒体已经成为了企业和品牌推广的重要渠道之一&#xff0c;海外KOL也在各个领域拥有着庞大的粉丝群体和影响力。然而&#xff0c;想要在众多内容中脱颖而出&#xff0c;提高推广内容的曝光率和点击率&#xff0c;仅仅依靠KOL的影响力是不够的&a…

论文阅读-THE GENERALIZATION GAP IN OFFLINE REINFORCEMENT LEARNING(ICLR 2024)

1.Motivation 本文希望比较online RL、offline RL、序列决策和BC等方法的泛化能力(对于不同的初始状态、transition functions、reward functions&#xff0c;现阶段offline RL训练的方式都是在同一个环境下的数据集进行训练)。实验发现offline的算法相较于online算法对新环境…

特别提醒:2024年软考连考时间再次调整!

近期&#xff0c;辽宁省发布了今年5月软考考试安排&#xff0c;有以下变化&#xff1a; 高级论文单独考试&#xff08;2小时&#xff09;&#xff0c;之前是案例和论文一起考&#xff0c;案例剩下的时间可以给论文用&#xff0c;现在是综合和案例一起考&#xff0c;综合剩下的…

菜鸡学习netty源码(一)——ServerBootStrap启动

1.概述 对于初学者而然,写一个netty本地进行测试的Server端和Client端,我们最先接触到的类就是ServerBootstrap和Bootstrap。这两个类都有一个公共的父类就是AbstractBootstrap. 那既然 ServerBootstrap和Bootstrap都有一个公共的分类,那就证明它们两个肯定有很多公共的职…

Redis集群.md

Redis集群 本章是基于 CentOS7 下的 Redis 集群教程&#xff0c;包括&#xff1a; 单机安装RedisRedis主从Redis分片集群 1.单机安装Redis 首先需要安装Redis所需要的依赖&#xff1a; yum install -y gcc tcl然后将课前资料提供的Redis安装包上传到虚拟机的任意目录&#xf…

如何在CentOS部署青龙面板并实现无公网IP远程访问本地面板

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Java双亲委派机制

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 概述 Java程序在运…

一起长锈:4 默认不可变的变量绑定与引用(从Java与C++转Rust之旅)

讲动人的故事,写懂人的代码 故事梗概:在她所维护的老旧Java系统即将被淘汰的危机边缘,这位在编程中总想快速完事的女程序员,希望能转岗到公司内部使用Rust语言的新项目组,因此开始自学Rust;然而,在掌握了Rust编程知识之后,为了通过Rust项目组的技术面试,使得转岗成功而…

sql优化思路

sql的优化经验 这里解释一下SQL语句的优化的原理 1.指明字段名称&#xff0c;可以尽量使用覆盖索引&#xff0c;避免回表查询&#xff0c;因此可以提高效率 2.字面意思&#xff0c;无需过多赘述。索引就是为了提高查询效率的。 3.图中两条sql直接可以使用union all 或者 uni…

羊大师分解,羊奶不为人知的健康奇迹

羊大师分解&#xff0c;羊奶不为人知的健康奇迹 羊奶是一种营养丰富的食品&#xff0c;拥有许多不为人知的健康益处。 益智健脑&#xff1a;羊奶中含有较高的钙、蛋白质、氨基酸、维生素、矿物质、无机盐、核酸等营养成分&#xff0c;这些都对大脑发育及增强智力有很大帮助。…

MySQL mydumper工具

目录 1. mydumper介绍 2. mydumper参数解释 3. 备份例子 3.1 备份全库(未包含其他) 3.2 备份全库(包含其他) 3.3 备份指定数据库(-B或--database) 3.4 导出指定表(-T或--tables-list) 3.5 只导出表结构&#xff0c;不导出表数据(-d或--no-data) 3.6 只导出表数据&#…