【数据结构】双向链表 超详细 (含:何时用一级指针或二级指针;指针域的指针是否要释放)

目录

一、简介

二. 双链表的实现

1.准备工作及其注意事项

1.1 先创建三个文件

1.2 注意事项:帮助高效记忆

1.3   关于什么时候 用 一级指针接收,什么时候用 二级指针接收?

1.4 释放节点时,要将节点地址 置为NULL,难道 节点内部的 指针域的指针 就不用置为 NULL吗? 

2.双链表的基本功能接口

2.1 初始化哨兵位

 2.2 链表的创建新节点接口

2.3 打印

3. 插入 接口

3.1 尾插法

3.2 头插法

3.3 在 pos 位置之后插入数据

4. 查找

5.删除  接口

5.1 尾删法

5.2 头删法

5.3  删除 pos 位置的数据

6. 销毁链表 接口

6.1 二级指针版 

6.2 一级指针版

7. 总代码概览

List.h

List.c

test.c

三. 顺序表和双向链表的优缺点分析



一、简介

        单向链表在解决那些需要大量查找前趋节点的问题时,效率较为低下,因为单向链表适合“从前往后”查找,并不适合“从后往前”查找。

(若想要看 单链表,可以点击跳转:【数据结构】单向链表实现 超详细)

如果要提高链表的查找效率,那  双向链表(双链表)无疑是首选。

双向链表:即为 字面上的意思是“双向”的链表,如下图所示。


       

  • 双向 指各个节点之间的逻辑关系是 双向的,该链表通常 有一个头节点 -- 称为 哨兵位
  • 概念: 当链表中只有哨兵位节点的时候,我们称该链表为空链表,即哨兵位是不能删除的
  • 从上图还可以看出,双向链表中每个节点包括一下3个部分,分别是:
  • 指针域(前驱节点 prev )、
  • 数据域(用于存储数据元素 data)
  • 指针域(后继节点 next)。

二. 双链表的实现

1.准备工作及其注意事项

1.1 先创建三个文件

 解释这三个文件的作用
 1、头文件List.h  是来声明接口函数,定义链表,将几个公共用到的库函数集合起来
 2、源文件List.c  是用来具体实现接口
 3、源文件test.c  用于接口的测试工作 ,即具体的使用场景

1.2 注意事项:帮助高效记忆

1. 传递指针,都要断言 不能为 NULL ,指针不能为空:assert(phead);
2. 存在 关系到  前趋节点prev 或 后继节点next  的 情况,

    可以 直接定义变量 Prev = .... Next = ....   便于 理清思路,不易乱
3. 所有的 删除接口 都需要 断言链表不能只有 哨兵位: assert(phead->next != phead); 
4. 尾删法: 记得双链表的找尾 不像 单链表需要循环去找尾!!ptail = phead->prev;

5. 初始化创建头节点:推荐使用 调用 创建节点接口  的 方法
5. 销毁接口:推荐使用 传一级指针的方法

1.3   关于什么时候 用 一级指针接收,什么时候用 二级指针接收?(不看水话,可以直接 看下面 总结 部分

(有点水话,实在不明白思路的可以看一下详细解说的 ”水话“)

         在 单向链表 中,有一个指向 第一个节点的指针 plist,由于 头插法等操作,可能会改变 第一个节点,则 plist 要对应进行 更新,而 要想直接改变一个变量(plist是指针变量)的值,需要传地址,plist 的 &plist 是 一级指针的地址,就要用 二级指针 来接收

         在 双向链表 中,存在 头节点 head ,即 哨兵位,哨兵是 不用进行 改变本身这个节点的 地址的!!

那就有铁铁要问了,不是还要改变 头节点 head( 哨兵位 ) 的指向,要指向 第一个 节点 或 尾节点 吗? 

回答:因为 我们 要改变的 是 双链表节点 结构 中 的 结构体成员变量prev 和 next ,改变 结构体的成员变量 只需要利用 结构体指针 p->prev = .... 或 p->next   = .... 就达到 修改 双链表节点指向 问题了,而你本身并不需要改变 一个节点的地址 p

总结 

总结:修改 双链表节点 的指向,是通过  修改节点结构体内部的 两个成员变量 来实现的只需要用到 结构体指针(即该节点的地址 p),找到  两个成员变量,即可完成修改,因而传递 一级指针就好,不用像 单链表那样还要 传递 一级指针的 地址

1.4 释放节点时,要将节点地址 置为NULL,难道 节点内部的 指针域的指针 就不用置为 NULL吗? 

回答:不用 因为节点的空间已经还给操作系统了 那个指针域的指针所占据的空间也还回去了 操作系统后续分配内存就会把那块空间覆盖掉 就不会又啥影响

2.双链表的基本功能接口

2.1 初始化哨兵位

初始化哨兵位 第一种方法:传入 指针,进行"加工" 成 哨兵位

// 初始化一个哨兵位:
void LTInit(LTNode** pphead)
{*pphead = (LTNode*)malloc(sizeof(LTNode));// 开辟空间是否成功的判断:一般malloc不会失败,失败证明内存不够了,写下面的证明算是好习惯,不写一般没问题if (*pphead == NULL) {perror("malloc fail!");exit(1);}(*pphead)->data = -1;(*pphead)->next = (*pphead)->prev = *pphead; // 哨兵位初识化,两个指针都指向自己
}

初始化哨兵位第二种方法:直接一个函数生成一个哨兵位,返回哨兵位就好,不用传指针

LTNode* LTInit()
{LTNode* phead = (LTNode*)malloc(sizeof(LTNode));// 开辟空间是否成功的判断if (phead == NULL) {perror("malloc fail!");exit(1);}phead->data = -1; // data 随便定,反正哨兵位data无效phead->next = phead->prev = phead;return phead;
}

初始化哨兵位 第二种方法的2.0 版本:因为哨兵位的初始化 和 2.2 创建新新节点的方法一样,可以合并调用


LTNode* LTInit_2()
{LTNode* phead = LTCreatNode(-1);return phead;
}

 2.2 链表的创建新节点接口

// 创建新节点
LTNode* LTCreatNode(LTDataType x)
{LTNode* newNode = (LTNode*)malloc(sizeof (LTNode));if (newNode == NULL){perror("malloc fail!");exit(1);}newNode->data = x;newNode->prev = newNode->next = newNode; // 都是 指向自己return newNode;
}

2.3 打印

双链表的打印
和 单链表打印一样,都需要循环,但是结束条件不一样
单链表以 pcur = NULL 为结束条件,双链表是 一种循环链表,头尾相连,不会有  pcur = NULL 的情况
正解:既然 哨兵位无有效数据,同时 循环一轮 还是 回到头(哨兵位),干脆:while (pcur != phead)


void LTPrint(LTNode* phead)
{assert(phead);// 哨兵位不能为空LTNode* pcur = phead->next;while (pcur != phead){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("\n");
}

3. 插入 接口

前言:

// 不需要改变哨兵位,则不需要传二级指针
// 如果需要修改哨兵位的话,则传二级指针

3.1 尾插法

  

双链表的 尾插法
双向链表尾插需不需要找尾的操作 ?

不需要 :ptail = head->prev;  注意这个 等价关系,便于理解下面的代码
尾插法 也称作 在哨兵位之前插入节点/最后一个有效节点之后插入数据

void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);// 哨兵位不能为空LTNode* newNode = LTCreatNode(x); LTNode* ptail = phead->prev;// 三者关系:phead->prev(即ptail)    newNode      phead// 处理顺序:先 newNode,  再 phead->prev(即ptail) ,最后  phead:否则会乱套// 先 newNodenewNode->next = phead;newNode->prev = ptail; // 就是  phead->prev// 再尾节点 ptail = head->prev  ;   ptail  -> next = head-> prev -> next;ptail->next = newNode;// 最后头节点 phead->prev = newNode; 
}

3.2 头插法

双链表的 头插法
注意:头插 是在第一个有效位置进行插入,不是在哨兵位 前面,由于 双链表的循环成环状的特性,若在哨兵位前面,就是尾插了

 

void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newNode = LTCreatNode(x);// 三者关系:phead   newNode   phead->next (即 第一个 有效节点,命名为Next) ;// 处理顺序:先 newNode,  再 phead->next(即 第一个 有效节点,命名为Next) ,最后  phead:否则会乱套LTNode* Next = phead->next; // 这里就是定义了个变量,便于梳理// 先 newNodenewNode->next = Next;newNode->prev = phead;// 再 phead->next(即 第一个 有效节点) Next->prev = newNode;// 最后头节点 phead->next = newNode;
}

3.3 在 pos 位置之后插入数据

在 pos 位置之后插入数据:要配合   4.查找   接口实现

和 头插法思路一样:只是将 哨兵的带头作用 换成了 pos

pos 是通过  4.查找  的接口找的void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);// 先创建新节点LTNode* newNode = LTCreatNode(x);// 关系三个节点:pos     newNode    pos->next// 先处理newNode,后 pos->next , 最后 pos // 注意三者的执行顺序 不能换!! LTNode* Next = pos->next; // 这里将 pos 的下一个节点(pos->next) 命名成 Next (避免和 next 混淆)newNode->next = Next;newNode->prev = pos;Next->prev = newNode;pos->next = newNode;
}

4. 查找

LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);// 下面是遍历 双链表的模板操作LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

5.删除  接口

5.1 尾删法

// 思路:让 倒数第二个 节点 next 指向 head ,head 指向 倒数第二个节点,最后 free 掉 ptail
// 尾节点前一个节点:ptail->prev = phead->prev->prev
// 尾节点:ptail = phead->prev
// "尾节点前一个节点" 指向 head哨兵位:ptail->prev->next = phead;
// head哨兵位 指向 "尾节点前一个节点" :phead->prev = ptail->prev;
// 最后 free 掉 ptail

 

// 尾删
void LTPopBack(LTNode* phead)
{assert(phead);//链表为空:只有一个哨兵位节点assert(phead->next != phead);LTNode* ptail = phead->prev;ptail->prev->next = phead;phead->prev = ptail->prev; free(ptail);ptail = NULL;
}

5.2 头删法

    //  被删除的 第一个有效节点:pFirst = phead->next; 

    //  下一个节点 pSecond = pFirst->next;
    //  先 下一个节点 指向 head : pSecond->prev = phead;
    //  后 head 指向 下一个节点 :phead->next = pSecond; 
    //  注意:因为对于循环链表来说, 第一个节点的下个节点 也可以是 哨兵位,所以 只存在一个有效节点,也是可以直接删除第一个节点的、

// 头删
// 删除的是 第一个有效节点
void LTPopFront(LTNode* phead)
{assert(phead); // 地址不能为NULLassert(phead->next != phead); // 链表不能只有 哨兵位LTNode* pFirst = phead->next;  // 要被删除的 第一个节点LTNode* pSecond = pFirst->next;pSecond->prev = phead;phead->next = pSecond;free(pFirst);pFirst = NULL;
}

5.3  删除 pos 位置的数据

删除 pos 位置的数据:要配合   4.查找   接口实现

void LTErase(LTNode* pos)
{assert(pos);// 关系三个节点: pos->prev      pos     pos->next     LTNode* Prev = pos->prev; //  pos 的前一个 LTNode* Next = pos->next; //  pos 的下一个Next->prev = Prev;Prev->next = Next;free(pos);pos = NULL;
}

6. 销毁链表 接口

更推荐 一级指针版:手动置为NULL

为了保持 接口的一致性:不然前面接口都是 一级指针,到这里突然 二级指针,当程序交给用户时,会增加记忆的成本

6.1 二级指针版 

// 思路:先将 有效节点删除,后删除哨兵位
// 删除有效节点, 要将下个节点保存起来,不然找不到
// 注意:这里 最后需要   改变  哨兵位 为NULL ,因而要传递地址,用二级指针接收
// 否则,传值 只会影响 形参,哨兵位 需要手动 置为NULL


void LTDestory(LTNode** pphead) 
{assert(pphead);// 指针本身不能为 空assert(*pphead); // 哨兵位 不能为 空LTNode* pcur = (*pphead)->next; // *pphead = phead 即哨兵位 ;还有记得 加括号while (pcur != *pphead){LTNode* next = pcur->next;free(pcur);pcur = next;}// 最后删除 哨兵位free(*pphead);*pphead = NULL;
}

6.2 一级指针版

// 双链表的 销毁:一级指针版:哨兵位 需要 手动额外 置为空

void LTDestory(LTNode* phead)
{assert(phead);// 哨兵位 不能为 空LTNode* pcur = (phead)->next; // *pphead = phead 即哨兵位 ;还有记得 加括号while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}// 最后释放掉 形参的指针:这里不是释放 哨兵位free(phead);phead = NULL;
}

7. 总代码概览

List.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>// 创建新节点
typedef int  LTDataType;
typedef struct LTistNode
{LTDataType data;struct LTistNode* prev;struct LTistNode* next;
}LTNode;// 创建新节点
LTNode* LTCreatNode(LTDataType x);// 初始化:生成哨兵位
LTNode* LTInit();// 打印函数
void LTPrint(LTNode* phead);// 尾插法
void LTPushBack(LTNode* phead, LTDataType x);// 头插法
void LTPushFront(LTNode* phead, LTDataType x);// 在 pos 之后插入数据
void LTInsert(LTNode* pos, LTDataType x);// 尾删法
void LTPopBack(LTNode* phead);// 头删法
void LTPopFront(LTNode* phead);// 删除 pos 位置节点
void LTErase(LTNode* pos);// 查找
LTNode* LTFind(LTNode* phead, LTDataType x);// 销毁
void LTDestory(LTNode* phead);

List.c

#include"List.h"// 创建新节点
LTNode* LTCreatNode(LTDataType x)
{LTNode* newNode = (LTNode*)malloc(sizeof(LTNode));if (newNode == NULL){perror("malloc fail!");exit(1);}newNode->data = x;newNode->prev = newNode->next = newNode;return newNode;
}// 初始化:生成哨兵位
LTNode* LTInit()
{LTNode* head = LTCreatNode(-1);return head;
}// 打印函数
void LTPrint(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("\n");
}// 尾插法
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);// 创建新节点LTNode* newNode = LTCreatNode(x);LTNode* ptail = phead->prev;// 三者关系:ptail   newNode   pheadnewNode->next = phead;newNode->prev = ptail;ptail->next = newNode;phead->prev = newNode;
}// 头插法
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);// 创建新节点LTNode* newNode = LTCreatNode(x);// 三者关系:phead   newNode   phead->next (定为Next);LTNode* Next = phead->next; // 这里就是定义了个变量,便于梳理newNode->next = Next;newNode->prev = phead;Next->prev = newNode;phead->next = newNode;
}// 查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x) return pcur;pcur = pcur->next;}return NULL;
}// 在 pos 之后插入数据:头插法实现逻辑一样
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);// 创建新节点LTNode* newNode = LTCreatNode(x);// 三者关系:pos   newNode   pso->next (定为Next);LTNode* Next = pos->next;newNode->next = Next;newNode->prev = pos;Next->prev = newNode;pos->next = newNode;
}// 尾删法:记得双链表的找尾 不像 单链表需要循环去找尾
void LTPopBack(LTNode* phead)
{assert(phead);assert(phead->next != phead);// 链表不能只有 哨兵位 (删除的接口 都要断言这条)// 三者关系:ptail->prev   ptail   headLTNode* ptail = phead->prev;ptail->prev->next = phead;phead->prev = ptail->prev;free(ptail);ptail = NULL;}// 头删法
void LTPopFront(LTNode* phead)
{assert(phead);assert(phead->next != phead); // 链表不能只有 哨兵位 (删除的接口 都要断言这条)// 三者关系:phead    phead->next    phead->next->next(命名为pSecond)LTNode* pFirst = phead->next;  // 要被删除的 第一个节点  一定要先保存下来!!LTNode* pSecond = pFirst->next;pSecond->prev = phead;phead->next = pSecond;free(pFirst);pFirst = NULL;
}// 删除 pos 位置节点
void LTErase(LTNode* pos)
{assert(pos);// 关系三个节点:pos->prev    pos     pos->nextLTNode* Prev = pos->prev; //  pos 的前一个 LTNode* Next = pos->next; //  pos 的下一个Next->prev = Prev;Prev->next = Next;free(pos);pos = NULL;
}// 销毁
void LTDestory(LTNode* phead)
{assert(phead);// 先全部删除有效节点,后删除头节点LTNode* pcur = phead->next;while (pcur != phead){LTNode* Next = pcur->next;free(pcur);pcur = Next;}// 最后释放掉 形参的指针:这里不是释放 哨兵位free(phead);phead = NULL;
}

test.c

#include"List.h"void LTTest()
{// 创建哨兵位LTNode* head = LTInit();  // 这里直接用 head 代表哨兵位:更直观一点,和 前面讲解用的 plist 是一样的// 尾插法LTPushBack(head, 1);LTPushBack(head, 2);LTPushBack(head, 3);LTPushBack(head, 4); // 1 -> 2 -> 3 -> 4 ->printf("测试尾插:");LTPrint(head);// 头插法LTPushFront(head, 5);LTPushFront(head, 6);// 6 -> 5 -> 1 -> 2 -> 3 -> 4 ->printf("测试头插:");LTPrint(head);// 查找//LTFind(head, 1);// 在 pos 之后插入数据:头插法实现逻辑一样LTNode* FindRet1 = LTFind(head, 1);LTInsert(FindRet1, 100);LTNode* FindRet2 = LTFind(head, 2);LTInsert(FindRet2, 200); // 6 -> 5 -> 1 -> 100 -> 2 -> 200 -> 3 -> 4 ->printf("测试pos 之后插入:");LTPrint(head);// 头删法LTPopFront(head);LTPopFront(head); // 1 -> 100 -> 2 -> 200 -> 3 -> 4 ->printf("测试头删:");LTPrint(head);// 尾删法:记得双链表的找尾 不像 单链表需要循环去找尾LTPopBack(head);LTPopBack(head); // 1 -> 100 -> 2 -> 200 ->printf("测试尾删:");LTPrint(head);// 删除 pos 位置节点LTNode* FindRet3 = LTFind(head, 1);LTErase(FindRet3); // 100 -> 2 -> 200 ->printf("测试删除 pos 位置节点:");LTPrint(head);// 双链表的 销毁:这里就不演示销毁了//LTDesTroy(head);//head= NULL;  //哨兵位 需要手动 置为NULL
}
int main()
{LTTest();return 0;
}

三. 顺序表和双向链表的优缺点分析

不同点

顺序表

链表(单链表)

存储空间

物理上⼀定连续

逻辑连续,但物理上不⼀定连续

机访问

支持O(1)

不支持:O(N)

任意位置插或者删除

可能要搬移素,率低O(N)

指针指向

插入

动态顺序表,空间够时要扩容

没有容的概念

应用场景

素高存储+频繁访问

任意位置插和删除频繁

完。

若上述文章有什么错误,欢迎各位大佬及时指出,我们共同进步!

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

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

相关文章

某赛通电子文档安全管理系统 UploadFileToCatalog SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

WordPress主题YIA的文章页评论内容为什么没有显示出来?

有些WordPress站长使用YIA主题后&#xff0c;在YIA主题设置的“基本”中没有开启“一键关闭评论功能”&#xff0c;而且文章也是允许评论的&#xff0c;但是评论框却不显示&#xff0c;最关键的是文章原本就有的评论内容也不显示&#xff0c;这是为什么呢&#xff1f; 根据YIA主…

决策树的相关知识点

&#x1f4d5;参考&#xff1a;ysu老师课件西瓜书 1.决策树的基本概念 【决策树】&#xff1a;决策树是一种描述对样本数据进行分类的树形结构模型&#xff0c;由节点和有向边组成。其中每个内部节点表示一个属性上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff…

和鲸科技与智谱AI达成合作,共建大模型生态基座

近日&#xff0c;上海和今信息科技有限公司&#xff08;简称“和鲸科技”&#xff09;与北京智谱华章科技有限公司&#xff08;简称“智谱AI”&#xff09;签订合作协议&#xff0c;双方将携手推动国产通用大模型的广泛应用与行业渗透&#xff0c;并积极赋能行业伙伴探索领域大…

回归预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络多变量回归预测

回归预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络多变量回归预测 目录 回归预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CPO-B…

electron项目在内网环境的linux环境下进行打包

Linux需要的文件: electron-v13.0.0-linux-x64.zip appimage-12.0.1.7z snap-template-electron-4.0-1-amd64.tar.7z 下载慢或者下载失败的情况可以手动下载以上electron文件复制到指定文件夹下&#xff1a; 1.electron-v13.0.0-linux-x64.zip 复制到~/.cache/electron/目录下…

什么是ACL?

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle OC…

IP 协议的相关特性

1. IP协议定义和特性 IP&#xff08;Internet Protocol&#xff09;协议是互联网中使用的网络协议之一&#xff0c;它的特性包括&#xff1a; 1. 无连接性&#xff1a;IP协议是一种无连接协议&#xff0c;每个数据包都是独立发送和处理的。因此&#xff0c;每个数据包都需要包含…

AI在线写作软件推荐:5款不可错过的写作工具

现在人工智能&#xff08;AI&#xff09;技术已经渗透到了各个领域&#xff0c;包括写作。AI在线写作软件的出现&#xff0c;为我们提供了更加高效、准确的写作工具。在本文中&#xff0c;我将向大家推荐5款功能强大的AI在线写作软件&#xff0c;这些软件可以帮助我们提高写作效…

python nacos-sdk-python 连接 nacos2.x版本,鉴权403解决办法

看nacos-sdk-python 的git项目提交记录&#xff0c;应该是已经解决了nacos2.x权限问题&#xff0c;但为什么还连接不上呢&#xff1f;因为最新代码&#xff0c;居然把以前鉴权代码删除了&#xff0c;具体原因不得而知。 解决办法&#xff1a; 1.把nacos-sdk-python里面params.…

redis 6.x集群搭建

redis6集群搭建 安装文件下载 redis-6.2.6.tar.gz 编译 tar -zxvf redis-6.2.6.tar.gz cd redis-6.2.6/ make MALLOClibc make install PREFIX/opt/soft/redis复制可执行文件 cp /opt/soft/redis/redis-cli /usr/bin/redis-cli cp /opt/soft/redis/redis-server /usr/bi…

小鱼深度产品测评之:阿里云自研PolarDBMySQL 版 Serverless,真正达到100%兼容 MySQL,分析性能达到开源数据400倍。

阿里云自研PolarDBMySQL 版 Serverless测评 一、 开箱二、 试用教程三、 使用感受3.1 查看资源包信息3.2 列表3.2.1 列表展示3.2.2 集群名称 3.3创建账号3.4 实例配置3.5 Serverless弹性压测3.5.1 遇到问题3.5.2 实操 四、 总结 一、 开箱 又到了体验新产品的时候了。 话不多说…

基于Python的招聘网站爬虫及可视化的设计与实现

摘要&#xff1a;现在&#xff0c;随着互联网网络的飞速发展&#xff0c;人们获取信息的最重要来源也由报纸、电视转变为了互联网。互联网的广泛应用使网络的数据量呈指数增长&#xff0c;让人们得到了更新、更完整的海量信息的同时&#xff0c;也使得人们在提取自己最想要的信…

tcpdump在手机上的使用

首先手机得root才可以&#xff0c;主要分析手机与手机的通信协议 我使用的是一加9pro&#xff0c; root方法参考一加全能盒子、一加全能工具箱官方网站——大侠阿木 (daxiaamu.com)https://optool.daxiaamu.com/index.php tcpdump&#xff0c;要安装在/data/local/tmp下要arm6…

JProfiler for Mac/win:深度探索Java性能的终极工具

随着Java应用的日益普及&#xff0c;性能优化成为开发人员的重要任务。在众多性能分析工具中&#xff0c;JProfiler以其强大的功能和直观的界面脱颖而出。本文将深入探讨JProfiler的优势&#xff0c;以及如何利用它来提升Java应用的性能。 一、JProfiler的核心优势 全面性能监…

备战蓝桥杯---搜索(DFS基础2)

下面我主要介绍一下深搜的简单应用吧&#xff1a; 下面是分析&#xff1a; 我们对每行遍历一下&#xff0c;跟求排列差不多。在判断条件上&#xff0c;我们可以放一个存列的数组&#xff0c;对于对角线的判断&#xff0c;我们可以发现在主对角线上&#xff0c;列数-dep为恒定值…

Swift 入门之自定义类型的模式匹配(Pattern Matching)

概览 小伙伴们都知道 Swift 是一门简洁、类型安全、极富表现力以及“性感迷人”的编程语言。 和大多数语言一样&#xff0c;在 Swift 中也有一些隐藏着的、不为人知的宝藏特性。利用它们我们可以极大增加撸码的愉悦和成就感。 其中&#xff0c;模式匹配&#xff08;Pattern …

Linux---动静态库

动静态库的相关概念 静态库&#xff08;.a&#xff09;&#xff1a;程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库动态库&#xff08;.so&#xff09;&#xff1a;程序在运行的时候才去链接动态库的代码&#xff0c;多个程序共享使用库的…

2024 年 11 款值得收藏的 iPhone 数据恢复软件和应用

数据丢失是任何人都无法承受的&#xff0c;因为它对每个人都至关重要。但是数据丢失的原因有很多&#xff0c;一些常见的原因是数据意外删除、设备被盗、iOS 越狱、硬件损坏、病毒感染等。我们列出了 iOS 的顶级恢复工具&#xff0c;其中将帮助您方便地恢复数据。 这是 11 款最…

k8s之安装部署及kuboard发布应用

目录 环境准备 系统规划 配置免密 将桥接的IPv4流量传递到iptables的链 系统基础配置 安装docker 安装docker及基础依赖 配置docker的仓库下载地址 部署k8s 添加阿里云的k8s源 安装kubeadm&#xff0c;kubelet和kubectl 初始化masteer节点 部署node节点 部署flanne…