【数据结构】第三站:单链表
目录
一、顺序表的缺陷
二、链表
1.链表的概念以及结构
2.链表的分类
3.单链表的逻辑结构与物理结构
三、单链表的实现
1.单链表的定义
2.单链表的接口定义
3.单链表的接口实现
四、单链表的实现完整代码
一、顺序表的缺陷
在上一篇文章中,我们了解了顺序表的结构以及他的接口的实现。但同时我们也发现了他的一些缺陷
问题:
1. 中间/头部的插入删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
思考:如何解决以上问题呢?下面给出了链表的结构来看看
二、链表
1.链表的概念以及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
2.链表的分类
链表一共有八种类型,他们可由是单向还是双向,是循环还是非循环,是带头结点还是不带头结点进行排列组合出八种结构
虽然有很多种结构,但是只有两种最为常用
无头单向非循环链表和带头双向循环链表
这里我们先只需要了解无头单向非循环链表,其他链表后续了解
3.单链表的逻辑结构与物理结构
如下图所示,是链表的实际的物理结构与逻辑结构。物理结构就是实实在在数据在内存中的变化,逻辑结构就是为了方便理解,形象化出来的
三、单链表的实现
1.单链表的定义
typedef int SLTDateType;typedef struct SListNode {SLTDateType data;struct SListNode* next; }SListNode;
如上代码所示,单链表有数据域和指针域两部分组成,指针是用于指向下一个结点的指针
2.单链表的接口定义
//单链表的打印 void SListPrint(SListNode* plist); //单链表的尾插 void SListPushBack(SListNode** pplist, SLTDateType x); //单链表的头插 void SListPushFront(SListNode** pplist, SLTDateType x); //单链表的尾删 void SListPopBack(SListNode** pplist); //单链表的头删 void SListPopFront(SListNode** pplist); //单链表的查找 SListNode* SListFind(SListNode* plist,SLTDateType x); //单链表在pos位置之后插入 void SListInsertAfter(SListNode* pos, SLTDateType x); //单链表在pos位置之前插入 void SListInsertPrev(SListNode** pplist, SListNode* pos, SLTDateType x); //单链表在pos之后删除 void SListEraseAfter(SListNode* pos); //单链表在pos之前删除 void SListErasePrev(SListNode** pplist, SListNode* pos); //单链表在pos位置删除 void SListErase(SListNode** pplist, SListNode* pos); //单链表的销毁 void SListDestroy(SListNode** pplist);
如上代码所示,是我们的单链表需要实现的接口,对于链表和顺序表一样都是为了实现数据的管理,区别就是前者是离散的,后者是连续的。我们的目的还是增删查改
3.单链表的接口实现
1.单链表的打印
//单链表的打印 void SListPrint(SListNode* plist) {SListNode* cur = plist;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n"); }
我们先来完成单链表的打印,对于单链表的打印,还是比较简单的,只需要先将头结点的指针给保存下来,然后依次去遍历单链表即可
2.单链表的尾插以及获取结点函数
//获取一个结点 SListNode* BuySListNode(SLTDateType x) {SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));if (tmp == NULL){perror("malloc fail");return;}tmp->next = NULL;tmp->data = x;return tmp; }//单链表的尾插 void SListPushBack(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);if (*pplist == NULL){*pplist = newnode;return;}SListNode* tail = (*pplist);while (tail->next != NULL){tail = tail->next;}tail->next = newnode; }
对于单链表的尾插,我们要特别注意了。pplist是单链表头结点的地址,所以一定不为空
首先是获取结点,我们为了方便,先直接将其封装为一个函数。
有了结点了,那么我们还要思考如何尾插,那么我们先完成一般情况,假设已经有了一个很长的单链表了,我们还想要继续尾插一个值,那么只需要先找到原来的尾结点,然后将尾结点和新结点进行链接即可
当然还是存在一些特殊情况的,比如说原来的单链表压根就没有尾结点,也就是说链表是空的,那么上面的一般方法肯定行不通,这里其实就需要特殊处理一下,直接将新节点和链表头链接起来即可
3.单链表的头插
//单链表的头插 void SListPushFront(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);SListNode* first = *pplist;*pplist = newnode;newnode->next = first; }
对于单链表的头插,就比较简单了,我们直接创建一个新结点,然后记录下原来的第一个结点的地址,然后让链表头与新节点链接起来,然后新节点与原来的第一个结点链接起来,这里我们会发下,其实是不需要处理空链表的情况的,这里体现了单链表适合头插
4.单链表的尾删
//单链表的尾删 void SListPopBack(SListNode** pplist) {assert(pplist);assert(*pplist);if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else{SListNode* tail = *pplist;while (tail->next->next != NULL){tail = tail->next;}free(tail->next);tail->next = NULL;} }
对于单链表的尾删,我们要想清楚了,首先是一般情况,当链表很长的时候,我们想要删除最后一个结点,那么得先找到前一个结点,然后释放最后一个结点,最后让前一个结点指向空。
我们还需要注意链表为空的状态,这个肯定是不可以删除的,所以我们直接断言掉。
然后是链表为一个结点的情况,如果链表只有一个结点,那么我们会发现,压根找不到前一个结点,所以我们也特殊处理,我们直接释放掉第一个结点,然后置空即可。
5.单链表的头删
//单链表的头删 void SListPopFront(SListNode** pplist) {assert(pplist);assert(*pplist);SListNode* second = (*pplist)->next;free(*pplist);*pplist = second; }
对于单链表的头删,我们同样断言掉链表为空的状态
然后我们需要做的就是记录第二个结点,然后释放原来的第一个结点,最后连接链表头和第二个结点。这样我们就实现了我们的目的。值得注意的是,我们发现链表的头删也是比较有优势的。
6.单链表的查找
//单链表的查找 SListNode* SListFind(SListNode* plist,SLTDateType x) {SListNode* cur = plist;while (cur != NULL){if (cur->data == x){return cur;}cur = cur->next;}return NULL; }
对于单链表的查找,这个也很简单,他和单链表的打印思路是一样的,不同的是只需要返回结点的地址即可。
7.单链表在pos位置之后的插入
//单链表在pos位置之后插入 void SListInsertAfter(SListNode* pos, SLTDateType x) {assert(pos);SListNode* newnode = BuySListNode(x);SListNode* next = pos->next;pos->next = newnode;newnode->next = next; }
单链表在pos位置之后的插入也是比较简单的,我们只需要先申请一个结点,然后记录pos位置的下一个结点,然后连接就可以了。值得注意的是,pos的位置不可能为空。因为他不是一个有效的结点地址
8.单链表在pos位置之前的插入
//单链表在pos位置之前插入 void SListInsertPrev(SListNode** pplist, SListNode* pos, SLTDateType x) {assert(pplist);assert(pos);assert(*pplist);SListNode* newnode = BuySListNode(x);if (*pplist == pos){*pplist = newnode;newnode->next = pos;}else{SListNode* prev = *pplist;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;} }
对于在pos位置之前的插入,确实是比较繁琐了。我们要思考,首先pos和pplist不可能为空,然后这个链表也是不可能为空链表的,至少也要有一个值。否则如果存在pos这个结点呢?
然后我们在来考虑一般情况,我们假设链表很长,在中间位置pos之前插入一个结点,那么毫无疑问的是,我们需要先申请一个结点,然后在通过遍历的方式要找到pos之前的那个结点。
有了这两个结点,那么我们就可以进行连接了。
然后是特殊情况,假如这个链表只有一个结点呢,这个结点正好就是pos,我们发现pos就没有前一个结点,其实这个就等效于头插。我们采用头插的方式即可
9.单链表在pos之后删除
//单链表在pos之后删除 void SListEraseAfter(SListNode* pos) {assert(pos);assert(pos->next);SListNode* next = pos->next;pos->next = next->next;free(next);next = NULL; }
单链表在pos之后删除的话,首先pos和pos的下一个结点不会为空,否则题目就矛盾了。所以我们得断言,然后我们直接记录pos 的下一个结点,然后连接pos和pos之后的结点。最后释放掉pos的下一个结点即可。
10.单链表在pos之前删除
//单链表在pos之前删除 void SListErasePrev(SListNode** pplist, SListNode* pos) {assert(pos);assert(pplist);assert(pos != *pplist);assert(*pplist);if ((*pplist)->next == pos){SListNode* del = *pplist;*pplist = pos;free(del);del = NULL;}else{SListNode* prev = *pplist;while (prev->next->next != pos){prev = prev->next;}SListNode* del = prev->next;prev->next = pos;free(del);del = NULL;} }
对于单链表在pos之前删除确实就比较复杂了,首先pos,pplist,*pplist肯定不可能为空,然后pos也绝不可以是头节点,所以pos!=*pplist
我们现在来思考,假设一般情况,链表很长,在中间位置是pos,删除pos的前一个结点,那么我们就需要找到pos 的前一个的前一个结点。然后记录pos的前一个结点。释放pos 的前一个结点,然后进行连接即可
对于特殊情况,也就是,pos在第二个结点上,这样我们无法找到pos的前一个的前一个结点,但是这个就是头删,我们直接采用类似的思路即可
11.单链表在pos位置的删除
//单链表在pos位置删除 void SListErase(SListNode** pplist, SListNode* pos) {assert(pplist);assert(pos);assert(*pplist);if (*pplist == pos){free(pos);*pplist = pos = NULL;}else{SListNode* prev = *pplist;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;} }
这个与上一个接口是基本一致的思路。不同的是,pos可以在头结点了,如果是头节点就是头删。
如果不是,就是先找到前一个结点,然后进行删除连接即可
12.单链表的销毁
//单链表的销毁 void SListDestroy(SListNode** pplist) {SListNode* cur = *pplist;while (cur != NULL){SListNode* next = cur->next;free(cur);cur = next;} }
对于单链表的销毁,这个也很简单,就直接遍历销毁即可,与打印和查找的思路是一致的
四、单链表的实现完整代码
SList.h文件
#pragma once#include<stdio.h> #include<stdlib.h> #include<malloc.h> #include<assert.h>typedef int SLTDateType;typedef struct SListNode {SLTDateType data;struct SListNode* next; }SListNode;//单链表的打印 void SListPrint(SListNode* plist); //单链表的尾插 void SListPushBack(SListNode** pplist, SLTDateType x); //单链表的头插 void SListPushFront(SListNode** pplist, SLTDateType x); //单链表的尾删 void SListPopBack(SListNode** pplist); //单链表的头删 void SListPopFront(SListNode** pplist); //单链表的查找 SListNode* SListFind(SListNode* plist,SLTDateType x); //单链表在pos位置之后插入 void SListInsertAfter(SListNode* pos, SLTDateType x); //单链表在pos位置之前插入 void SListInsertPrev(SListNode** pplist, SListNode* pos, SLTDateType x); //单链表在pos之后删除 void SListEraseAfter(SListNode* pos); //单链表在pos之前删除 void SListErasePrev(SListNode** pplist, SListNode* pos); //单链表在pos位置删除 void SListErase(SListNode** pplist, SListNode* pos); //单链表的销毁 void SListDestroy(SListNode** pplist);
SList.c文件
#define _CRT_SECURE_NO_WARNINGS 1 #include"SList.h"//单链表的打印 void SListPrint(SListNode* plist) {SListNode* cur = plist;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n"); }//获取一个结点 SListNode* BuySListNode(SLTDateType x) {SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));if (tmp == NULL){perror("malloc fail");return;}tmp->next = NULL;tmp->data = x;return tmp; }//单链表的尾插 void SListPushBack(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);if (*pplist == NULL){*pplist = newnode;return;}SListNode* tail = (*pplist);while (tail->next != NULL){tail = tail->next;}tail->next = newnode; }//单链表的头插 void SListPushFront(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);SListNode* first = *pplist;*pplist = newnode;newnode->next = first; }//单链表的尾删 void SListPopBack(SListNode** pplist) {assert(pplist);assert(*pplist);if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else{SListNode* tail = *pplist;while (tail->next->next != NULL){tail = tail->next;}free(tail->next);tail->next = NULL;} }//单链表的头删 void SListPopFront(SListNode** pplist) {assert(pplist);assert(*pplist);SListNode* second = (*pplist)->next;free(*pplist);*pplist = second; }//单链表的查找 SListNode* SListFind(SListNode* plist,SLTDateType x) {SListNode* cur = plist;while (cur != NULL){if (cur->data == x){return cur;}cur = cur->next;}return NULL; } //单链表在pos位置之后插入 void SListInsertAfter(SListNode* pos, SLTDateType x) {assert(pos);SListNode* newnode = BuySListNode(x);SListNode* next = pos->next;pos->next = newnode;newnode->next = next; } //单链表在pos位置之前插入 void SListInsertPrev(SListNode** pplist, SListNode* pos, SLTDateType x) {assert(pplist);assert(pos);assert(*pplist);SListNode* newnode = BuySListNode(x);if (*pplist == pos){*pplist = newnode;newnode->next = pos;}else{SListNode* prev = *pplist;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;} } //单链表在pos之后删除 void SListEraseAfter(SListNode* pos) {assert(pos);assert(pos->next);SListNode* next = pos->next;pos->next = next->next;free(next);next = NULL; } //单链表在pos之前删除 void SListErasePrev(SListNode** pplist, SListNode* pos) {assert(pos);assert(pplist);assert(pos != *pplist);assert(*pplist);if ((*pplist)->next == pos){SListNode* del = *pplist;*pplist = pos;free(del);del = NULL;}else{SListNode* prev = *pplist;while (prev->next->next != pos){prev = prev->next;}SListNode* del = prev->next;prev->next = pos;free(del);del = NULL;} } //单链表在pos位置删除 void SListErase(SListNode** pplist, SListNode* pos) {assert(pplist);assert(pos);assert(*pplist);if (*pplist == pos){free(pos);*pplist = pos = NULL;}else{SListNode* prev = *pplist;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;} } //单链表的销毁 void SListDestroy(SListNode** pplist) {SListNode* cur = *pplist;while (cur != NULL){SListNode* next = cur->next;free(cur);cur = next;} }
Test.c文件
#define _CRT_SECURE_NO_WARNINGS 1#include"Slist.h"void TestSList1() {SListNode* phead = NULL;SListPushBack(&phead, 1);SListPushBack(&phead, 2);SListPushBack(&phead, 3);SListPushBack(&phead, 4);SListPushBack(&phead, 5);SListPrint(phead);SListPushFront(&phead, 6);SListPushFront(&phead, 7);SListPushFront(&phead, 8);SListPushFront(&phead, 9);SListPushFront(&phead, 10);SListPrint(phead);SListPopBack(&phead);SListPopBack(&phead);SListPopBack(&phead);SListPopBack(&phead);SListPrint(phead);SListPopBack(&phead);SListPrint(phead);} void TestSList2() {SListNode* phead = NULL;SListPushBack(&phead, 1);SListPushBack(&phead, 2);SListPushBack(&phead, 3);SListPushBack(&phead, 4);SListPushBack(&phead, 5);SListPrint(phead);SListPopFront(&phead);SListPopFront(&phead);SListPopFront(&phead);SListPopFront(&phead);SListPrint(phead);SListPopFront(&phead);SListPrint(phead); } void TestSList3() {SListNode* phead = NULL;SListPushBack(&phead, 1);SListPushBack(&phead, 2);SListPushBack(&phead, 3);SListPushBack(&phead, 4);SListPushBack(&phead, 5);SListPrint(phead);SListNode* pos = SListFind(phead, 3);pos->data = 100;SListPrint(phead);SListInsertAfter(pos, 200);SListInsertAfter(pos, 300);SListInsertAfter(pos, 400);SListInsertAfter(pos, 500);SListPrint(phead);SListNode* pos2 = SListFind(phead, 1);SListInsertPrev(&phead, pos2, 101);SListInsertPrev(&phead, pos2, 102);SListInsertPrev(&phead, pos2, 103);SListInsertPrev(&phead, pos2, 104);SListPrint(phead);SListEraseAfter(pos2);SListEraseAfter(pos2);SListEraseAfter(pos2);SListPrint(phead);}void TestSList4() {SListNode* phead = NULL;SListPushBack(&phead, 1);SListPushBack(&phead, 2);SListPushBack(&phead, 3);SListPushBack(&phead, 4);SListPushBack(&phead, 5);SListPrint(phead);SListNode* pos = SListFind(phead, 5);SListErasePrev(&phead, pos);SListPrint(phead);SListErasePrev(&phead, pos);SListPrint(phead);SListErase(&phead, pos);SListPrint(phead);SListDestroy(&phead); }int main() {//TestSList1();//TestSList2();//TestSList3();TestSList4();return 0; }
本节内容到此位置,感谢您的阅读
如果对你有帮助的话,不要忘记点赞加收藏哦!!!
相关文章:

【数据结构】第三站:单链表
目录 一、顺序表的缺陷 二、链表 1.链表的概念以及结构 2.链表的分类 3.单链表的逻辑结构与物理结构 三、单链表的实现 1.单链表的定义 2.单链表的接口定义 3.单链表的接口实现 四、单链表的实现完整代码 一、顺序表的缺陷 在上一篇文章中,我们了解了顺序…...

【蓝桥杯2020】七段码
【题目描述】 七段码 HUSTOJ 题目导出文件 [蓝桥杯2020] 第十一届蓝桥杯第二次省赛—填空题E题 七段码 小蓝要用七段码数码管来表示一种特殊的文字。 上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二 极管,分别标记为 a, b, c,…...

Spark读取JDBC调优
Spark读取JDBC调优,如何调参一、场景构建二、参数设置1.灵活运用分区列实际问题:工作中需要读取一个存放了三四年历史数据的pg数仓表(缺少主键id),需要将数据同步到阿里云 MC中,Spark在使用JDBC读取关系型数…...

【文心一言】什么是文心一言,如何获得内测和使用方法。
文心一言什么是文心一言怎么获得内测资格接下来就给大家展示一下文学创作商业文案创作数理逻辑推算中文理解多模态生成用python写一个九九乘法表写古诗前言: 🏠个人主页:以山河作礼。 📝📝:本文章是帮助大家了解文心…...

CentOS8服务篇10:FTP服务器配置与管理
一、安装与启动FTP服务器 1、安装VSFTP服务器所需要的安装包 #yum -y install vsftpd 2、查看配置文件参数 Vim /etc/vsftpd/vsftpd.conf (1)是否允许匿名登录 anonymous_enableYES 该行用于控制是否允许匿名用户登录。 (2&…...
笔试强训3.14
一、选择题 1.以下说法错误的是(C) A.数组是一个对象 B.数组不是一种原生类 C.数组的大小可以任意改变 D.在Java中,数组存储在堆中连续内存空间里 相关知识点:原生/内置数组是那八个,其他的都是引用的,借…...
elasticsearch 环境搭建和基本操作
参考资料 适合后端编程人员的elasticsearch快速实战教程 ElasticSearch最新实战教程 ElasticSearch配套笔记 自制搜索引擎 https://www.elastic.co/guide/en/elasticsearch/reference/7.17/setup.html restful风格的api REST 设计风格 例如以下springboot示例 RestContr…...

IDEA操作:Springboot项目打包为jar包并运行
在IDEA环境下对Springboot项目打包为jar包且在terminal运行操作 1、 2、 3、注意:在项目目录里创建一个用来存放jar包的文件夹(res),该路径不能使用IDEA设置的默认路径,必须手动创建。 4、 5、点击ok后加载运行包 (8…...

原理底层计划---JVM
二、JVM对空间大小怎么配置?各区域怎么划? 新生代:短时间生成,可以马上回收 老生代:少部分对象会存在很久,回收策略应不同 三、JVM哪些内存区域会发生内存溢出(程序计数器不会) …...
CSDN-猜年龄、纸牌三角形、排他平方数
猜年龄 原题链接:https://edu.csdn.net/skill/practice/algorithm-a413078fb6e74644b8c9f6e28896e377/2258 美国数学家维纳(N.Wiener)智力早熟,11岁就上了大学。他曾在1935~1936年应邀来中国清华大学讲学。 一次,他参加某个重要会议…...

【Linux】软件包管理器 yum
什么是软件包和软件包管理器 在 Linux 下需要安装软件时, 最原始的办法就是下载到程序的源代码, 进行编译得到可执行程序。但是这样太麻烦了,所以有些人就把一些常用的软件提前编译好, 做成软件包 ( 就相当于windows上的软件安装程序)放在服…...

一天吃透TCP面试八股文
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…...
zzu天梯赛选拔
C. NANA去上课 — 简单数学 需要记录上一步处在哪个位置 然后判断如果是同一侧移动距离就是abs(x1 - x2) 如果不同就是x1 x2 #include <iostream> #include <cmath> using namespace std; #define int long long signed main() {int n; c…...

【C语言】一篇让你彻底吃透(结构体与结构体位段)
本章重点 主要讲解结构体和位移动的使用和定义与声明,并且结构体和位段在内存中是如何存储的。 文章目录结构体结构体类型的声明结构体特殊的声明结构体变量的定义和初始化结构体成员的访问结构的自引用结构体内存对齐结构体传参位段什么是位段位段的内存分配位段的…...

数据结构之二叉树构建、广度/深度优先(前序、中序、后序)遍历
一、二叉树 1.1 树 说到树,我们暂时忘记学习,来看一下大自然的树: 哈哈 以上照片是自己拍的,大家凑合看看 回归正题,那么在数据结构中,树是什么呢,通过上面的图片大家也可以理解 树是一种非…...

“国产版ChatGPT”文心一言发布会现场Demo硬核复现
文章目录前言实验结果一、文学创作问题1 :《三体》的作者是哪里人?问题2:可以总结下三体的核心内容吗?如果要续写的话,可以从哪些角度出发?问题3:如何从哲学角度来进行续写?问题4:电…...

202304读书笔记|《不被定义的女孩》——做最真实最漂亮的自己,依心而行
202304读书笔记|《不被定义的女孩》——做最真实最漂亮的自己,依心而行《不被定义的女孩》作者ASEN,很棒的书。处处透露着洒脱,通透,悦己,阅世界的自由的氛围和态度! 部分节选如下: 让自己活得…...

SpringBoot帮你优雅的关闭WEB应用程序
Graceful shutdown 应用 Graceful shutdown说明 Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and servlet-based web applications. It occurs as part of closing the applica…...

递归与递推
递归 直白理解:函数在其内部调用自身(自己调用自己)所有递归都可以采用递归搜索树来理解递归的特点: 一般来说代码较为简短,但是理解难度大一般时间和空间消耗较大,容易产生重复计算,可能爆栈 …...

使用<style scoped>导致的样式问题
问题描述: 今天使用开源组件库TDesign的自动补全组件时,遇到了一个样式失效问题,一开始怎么也找不到问题出在哪,后面一个偶然去掉了scoped,竟然发现样式竟然正常了,具体原因不知道在哪,有大佬知…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...